// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
 *   Copyright (C) 1996-2016, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 */

package com.ibm.icu.util;

import com.ibm.icu.impl.CalType;
import com.ibm.icu.impl.CalendarUtil;
import com.ibm.icu.impl.Grego;
import com.ibm.icu.impl.ICUCache;
import com.ibm.icu.impl.ICUData;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.SimpleCache;
import com.ibm.icu.impl.SimpleFormatterImpl;
import com.ibm.icu.impl.SoftCache;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DateFormatSymbols;
import com.ibm.icu.text.DateTimePatternGenerator;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.BasicTimeZone.LocalOption;
import com.ibm.icu.util.ULocale.Category;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;
import java.util.MissingResourceException;

/**
 * {@icuenhanced java.util.Calendar}.{@icu _usage_}
 *
 * <p><code>Calendar</code> is an abstract base class for converting between a <code>Date</code>
 * object and a set of integer fields such as <code>YEAR</code>, <code>MONTH</code>, <code>DAY
 * </code>, <code>HOUR</code>, and so on. (A <code>Date</code> object represents a specific instant
 * in time with millisecond precision. See {@link Date} for information about the <code>Date</code>
 * class.)
 *
 * <p>Subclasses of <code>Calendar</code> interpret a <code>Date</code> according to the rules of a
 * specific calendar system. ICU4J contains several subclasses implementing different international
 * calendar systems.
 *
 * <p>Like other locale-sensitive classes, <code>Calendar</code> provides a class method, <code>
 * getInstance</code>, for getting a generally useful object of this type. <code>Calendar</code>'s
 * <code>getInstance</code> method returns a calendar of a type appropriate to the locale, whose
 * time fields have been initialized with the current date and time:
 *
 * <blockquote>
 *
 * <pre>Calendar rightNow = Calendar.getInstance()</pre>
 *
 * </blockquote>
 *
 * <p>When a <code>ULocale</code> is used by <code>getInstance</code>, its '<code>calendar</code>'
 * tag and value are retrieved if present. If a recognized value is supplied, a calendar is provided
 * and configured as appropriate. Currently recognized tags are "buddhist", "chinese", "coptic",
 * "ethiopic", "gregorian", "hebrew", "islamic", "islamic-civil", "japanese", and "roc". For
 * example:
 *
 * <blockquote>
 *
 * <pre>Calendar cal = Calendar.getInstance(new ULocale("en_US@calendar=japanese"));</pre>
 *
 * </blockquote>
 *
 * will return an instance of JapaneseCalendar (using en_US conventions for minimum days in first
 * week, start day of week, et cetera).
 *
 * <p>A <code>Calendar</code> object can produce all the time field values needed to implement the
 * date-time formatting for a particular language and calendar style (for example,
 * Japanese-Gregorian, Japanese-Traditional). <code>Calendar</code> defines the range of values
 * returned by certain fields, as well as their meaning. For example, the first month of the year
 * has value <code>MONTH</code> == <code>JANUARY</code> for all calendars. Other values are defined
 * by the concrete subclass, such as <code>ERA</code> and <code>YEAR</code>. See individual field
 * documentation and subclass documentation for details.
 *
 * <p>When a <code>Calendar</code> is <em>lenient</em>, it accepts a wider range of field values
 * than it produces. For example, a lenient <code>GregorianCalendar</code> interprets <code>MONTH
 * </code> == <code>JANUARY</code>, <code>DAY_OF_MONTH</code> == 32 as February 1. A non-lenient
 * <code>GregorianCalendar</code> throws an exception when given out-of-range field settings. When
 * calendars recompute field values for return by <code>get()</code>, they normalize them. For
 * example, a <code>GregorianCalendar</code> always produces <code>DAY_OF_MONTH</code> values
 * between 1 and the length of the month.
 *
 * <p><code>Calendar</code> defines a locale-specific seven day week using two parameters: the first
 * day of the week and the minimal days in first week (from 1 to 7). These numbers are taken from
 * the locale resource data when a <code>Calendar</code> is constructed. They may also be specified
 * explicitly through the API.
 *
 * <p>When setting or getting the <code>WEEK_OF_MONTH</code> or <code>WEEK_OF_YEAR</code> fields,
 * <code>Calendar</code> must determine the first week of the month or year as a reference point.
 * The first week of a month or year is defined as the earliest seven day period beginning on <code>
 * getFirstDayOfWeek()</code> and containing at least <code>getMinimalDaysInFirstWeek()</code> days
 * of that month or year. Weeks numbered ..., -1, 0 precede the first week; weeks numbered 2, 3,...
 * follow it. Note that the normalized numbering returned by <code>get()</code> may be different.
 * For example, a specific <code>Calendar</code> subclass may designate the week before week 1 of a
 * year as week <em>n</em> of the previous year.
 *
 * <p>When computing a <code>Date</code> from time fields, some special circumstances may arise:
 * there may be insufficient information to compute the <code>Date</code> (such as only year and
 * month but no day in the month), there may be inconsistent information (such as "Tuesday, July 15,
 * 1996" -- July 15, 1996 is actually a Monday), or the input time might be ambiguous because of
 * time zone transition.
 *
 * <p><strong>Insufficient information.</strong> The calendar will use default information to
 * specify the missing fields. This may vary by calendar; for the Gregorian calendar, the default
 * for a field is the same as that of the start of the epoch: i.e., YEAR = 1970, MONTH = JANUARY,
 * DATE = 1, etc.
 *
 * <p><strong>Inconsistent information.</strong> If fields conflict, the calendar will give
 * preference to fields set more recently. For example, when determining the day, the calendar will
 * look for one of the following combinations of fields. The most recent combination, as determined
 * by the most recently set single field, will be used.
 *
 * <blockquote>
 *
 * <pre>
 * MONTH + DAY_OF_MONTH
 * MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
 * MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
 * DAY_OF_YEAR
 * DAY_OF_WEEK + WEEK_OF_YEAR</pre>
 *
 * </blockquote>
 *
 * For the time of day:
 *
 * <blockquote>
 *
 * <pre>
 * HOUR_OF_DAY
 * AM_PM + HOUR</pre>
 *
 * </blockquote>
 *
 * <p><strong>Ambiguous Wall Clock Time.</strong> When time offset from UTC has changed, it produces
 * an ambiguous time slot around the transition. For example, many US locations observe daylight
 * saving time. On the date switching to daylight saving time in US, wall clock time jumps from
 * 12:59 AM (standard) to 2:00 AM (daylight). Therefore, wall clock time from 1:00 AM to 1:59 AM do
 * not exist on the date. When the input wall time fall into this missing time slot, the ICU
 * Calendar resolves the time using the UTC offset before the transition by default. In this
 * example, 1:30 AM is interpreted as 1:30 AM standard time (non-exist), so the final result will be
 * 2:30 AM daylight time.
 *
 * <p>On the date switching back to standard time, wall clock time is moved back one hour at 2:00
 * AM. So wall clock time from 1:00 AM to 1:59 AM occur twice. In this case, the ICU Calendar
 * resolves the time using the UTC offset after the transition by default. For example, 1:30 AM on
 * the date is resolved as 1:30 AM standard time.
 *
 * <p>Ambiguous wall clock time resolution behaviors can be customized by Calendar APIs {@link
 * #setRepeatedWallTimeOption(int)} and {@link #setSkippedWallTimeOption(int)}. These methods are
 * available in ICU 49 or later versions.
 *
 * <p><strong>Note:</strong> for some non-Gregorian calendars, different fields may be necessary for
 * complete disambiguation. For example, a full specification of the historial Arabic astronomical
 * calendar requires year, month, day-of-month <em>and</em> day-of-week in some cases.
 *
 * <p><strong>Note:</strong> There are certain possible ambiguities in interpretation of certain
 * singular times, which are resolved in the following ways:
 *
 * <ol>
 *   <li>24:00:00 "belongs" to the following day. That is, 23:59 on Dec 31, 1969 &lt; 24:00 on Jan
 *       1, 1970 &lt; 24:01:00 on Jan 1, 1970
 *   <li>Although historically not precise, midnight also belongs to "am", and noon belongs to "pm",
 *       so on the same day, 12:00 am (midnight) &lt; 12:01 am, and 12:00 pm (noon) &lt; 12:01 pm
 * </ol>
 *
 * <p>The date or time format strings are not part of the definition of a calendar, as those must be
 * modifiable or overridable by the user at runtime. Use {@link DateFormat} to format dates.
 *
 * <p><strong>Field manipulation methods</strong>
 *
 * <p><code>Calendar</code> fields can be changed using three methods: <code>set()</code>, <code>
 * add()</code>, and <code>roll()</code>.
 *
 * <p><strong><code>set(f, value)</code></strong> changes field <code>f</code> to <code>value</code>
 * . In addition, it sets an internal member variable to indicate that field <code>f</code> has been
 * changed. Although field <code>f</code> is changed immediately, the calendar's milliseconds is not
 * recomputed until the next call to <code>get()</code>, <code>getTime()</code>, or <code>
 * getTimeInMillis()</code> is made. Thus, multiple calls to <code>set()</code> do not trigger
 * multiple, unnecessary computations. As a result of changing a field using <code>set()</code>,
 * other fields may also change, depending on the field, the field value, and the calendar system.
 * In addition, <code>get(f)</code> will not necessarily return <code>value</code> after the fields
 * have been recomputed. The specifics are determined by the concrete calendar class.
 *
 * <p><em>Example</em>: Consider a <code>GregorianCalendar</code> originally set to August 31, 1999.
 * Calling <code>set(Calendar.MONTH,
 * Calendar.SEPTEMBER)</code> sets the calendar to September 31, 1999. This is a temporary internal
 * representation that resolves to October 1, 1999 if <code>getTime()</code>is then called. However,
 * a call to <code>set(Calendar.DAY_OF_MONTH, 30)</code> before the call to <code>getTime()</code>
 * sets the calendar to September 30, 1999, since no recomputation occurs after <code>set()</code>
 * itself.
 *
 * <p><strong><code>add(f, delta)</code></strong> adds <code>delta</code> to field <code>f</code>.
 * This is equivalent to calling <code>set(f,
 * get(f) + delta)</code> with two adjustments:
 *
 * <blockquote>
 *
 * <p><strong>Add rule 1</strong>. The value of field <code>f</code> after the call minus the value
 * of field <code>f</code> before the call is <code>delta</code>, modulo any overflow that has
 * occurred in field <code>f</code>. Overflow occurs when a field value exceeds its range and, as a
 * result, the next larger field is incremented or decremented and the field value is adjusted back
 * into its range.
 *
 * <p><strong>Add rule 2</strong>. If a smaller field is expected to be invariant, but &nbsp; it is
 * impossible for it to be equal to its prior value because of changes in its minimum or maximum
 * after field <code>f</code> is changed, then its value is adjusted to be as close as possible to
 * its expected value. A smaller field represents a smaller unit of time. <code>HOUR</code> is a
 * smaller field than <code>DAY_OF_MONTH</code>. No adjustment is made to smaller fields that are
 * not expected to be invariant. The calendar system determines what fields are expected to be
 * invariant.
 *
 * </blockquote>
 *
 * <p>In addition, unlike <code>set()</code>, <code>add()</code> forces an immediate recomputation
 * of the calendar's milliseconds and all fields.
 *
 * <p><em>Example</em>: Consider a <code>GregorianCalendar</code> originally set to August 31, 1999.
 * Calling <code>add(Calendar.MONTH,
 * 13)</code> sets the calendar to September 30, 2000. <strong>Add rule 1</strong> sets the <code>
 * MONTH</code> field to September, since adding 13 months to August gives September of the next
 * year. Since <code>DAY_OF_MONTH</code> cannot be 31 in September in a <code>GregorianCalendar
 * </code>, <strong>add rule 2</strong> sets the <code>DAY_OF_MONTH</code> to 30, the closest
 * possible value. Although it is a smaller field, <code>DAY_OF_WEEK</code> is not adjusted by rule
 * 2, since it is expected to change when the month changes in a <code>GregorianCalendar</code>.
 *
 * <p><strong><code>roll(f, delta)</code></strong> adds <code>delta</code> to field <code>f</code>
 * without changing larger fields. This is equivalent to calling <code>add(f, delta)</code> with the
 * following adjustment:
 *
 * <blockquote>
 *
 * <p><strong>Roll rule</strong>. Larger fields are unchanged after the call. A larger field
 * represents a larger unit of time. <code>DAY_OF_MONTH</code> is a larger field than <code>HOUR
 * </code>.
 *
 * </blockquote>
 *
 * <p><em>Example</em>: Consider a <code>GregorianCalendar</code> originally set to August 31, 1999.
 * Calling <code>roll(Calendar.MONTH,
 * 8)</code> sets the calendar to April 30, <strong>1999</strong>. Add rule 1 sets the <code>MONTH
 * </code> field to April. Using a <code>GregorianCalendar</code>, the <code>DAY_OF_MONTH</code>
 * cannot be 31 in the month April. Add rule 2 sets it to the closest possible value, 30. Finally,
 * the <strong>roll rule</strong> maintains the <code>YEAR</code> field value of 1999.
 *
 * <p><em>Example</em>: Consider a <code>GregorianCalendar</code> originally set to Sunday June 6,
 * 1999. Calling <code>roll(Calendar.WEEK_OF_MONTH, -1)</code> sets the calendar to Tuesday June 1,
 * 1999, whereas calling <code>add(Calendar.WEEK_OF_MONTH, -1)</code> sets the calendar to Sunday
 * May 30, 1999. This is because the roll rule imposes an additional constraint: The <code>MONTH
 * </code> must not change when the <code>WEEK_OF_MONTH</code> is rolled. Taken together with add
 * rule 1, the resultant date must be between Tuesday June 1 and Saturday June 5. According to add
 * rule 2, the <code>DAY_OF_WEEK</code>, an invariant when changing the <code>WEEK_OF_MONTH</code>,
 * is set to Tuesday, the closest possible value to Sunday (where Sunday is the first day of the
 * week).
 *
 * <p><strong>Usage model</strong>. To motivate the behavior of <code>add()</code> and <code>roll()
 * </code>, consider a user interface component with increment and decrement buttons for the month,
 * day, and year, and an underlying <code>GregorianCalendar</code>. If the interface reads January
 * 31, 1999 and the user presses the month increment button, what should it read? If the underlying
 * implementation uses <code>set()</code>, it might read March 3, 1999. A better result would be
 * February 28, 1999. Furthermore, if the user presses the month increment button again, it should
 * read March 31, 1999, not March 28, 1999. By saving the original date and using either <code>add()
 * </code> or <code>roll()</code>, depending on whether larger fields should be affected, the user
 * interface can behave as most users will intuitively expect.
 *
 * <p><b>Note:</b> You should always use {@link #roll roll} and {@link #add add} rather than
 * attempting to perform arithmetic operations directly on the fields of a {@code Calendar}. It is
 * quite possible for {@code Calendar} subclasses to have fields with non-linear behavior, for
 * example missing months or days during non-leap years. The subclasses' {@code add} and {@code
 * roll} methods will take this into account, while simple arithmetic manipulations may give invalid
 * results.
 *
 * <p style="font-size:x-large;font-weight:bold">Calendar Architecture in ICU4J
 *
 * <p>Recently the implementation of <code>Calendar</code> has changed significantly in order to
 * better support subclassing. The original <code>Calendar</code> class was designed to support
 * subclassing, but it had only one implemented subclass, <code>GregorianCalendar</code>. With the
 * implementation of several new calendar subclasses, including the <code>BuddhistCalendar</code>,
 * <code>ChineseCalendar</code>, <code>HebrewCalendar</code>, <code>IslamicCalendar</code>, and
 * <code>JapaneseCalendar</code>, the subclassing API has been reworked thoroughly. This section
 * details the new subclassing API and other ways in which <code>com.ibm.icu.util.Calendar</code>
 * differs from <code>java.util.Calendar</code>.
 *
 * <p style="font-size:large;font-weight:bold">Changes
 *
 * <p>Overview of changes between the classic <code>Calendar</code> architecture and the new
 * architecture.
 *
 * <ul>
 *   <li>The <code>fields[]</code> array is <code>private</code> now instead of <code>protected
 *       </code>. Subclasses must access it using the methods {@link #internalSet} and {@link
 *       #internalGet}. <b>Motivation:</b> Subclasses should not directly access data members.
 *   <li>The <code>time</code> long word is <code>private</code> now instead of <code>protected
 *       </code>. Subclasses may access it using the method {@link #internalGetTimeInMillis}, which
 *       does not provoke an update. <b>Motivation:</b> Subclasses should not directly access data
 *       members.
 *   <li>The scope of responsibility of subclasses has been drastically reduced. As much
 *       functionality as possible is implemented in the <code>Calendar</code> base class. As a
 *       result, it is much easier to subclass <code>Calendar</code>. <b>Motivation:</b> Subclasses
 *       should not have to reimplement common code. Certain behaviors are common across calendar
 *       systems: The definition and behavior of week-related fields and time fields, the arithmetic
 *       ({@link #add(int, int) add} and {@link #roll(int, int) roll}) behavior of many fields, and
 *       the field validation system.
 *   <li>The subclassing API has been completely redesigned.
 *   <li>The <code>Calendar</code> base class contains some Gregorian calendar algorithmic support
 *       that subclasses can use (specifically in {@link #handleComputeFields}). Subclasses can use
 *       the methods <code>getGregorianXxx()</code> to obtain precomputed values. <b>Motivation:</b>
 *       This is required by all <code>Calendar</code> subclasses in order to implement consistent
 *       time zone behavior, and Gregorian-derived systems can use the already computed data.
 *   <li>The <code>FIELD_COUNT</code> constant has been removed. Use {@link #getFieldCount}. In
 *       addition, framework API has been added to allow subclasses to define additional fields.
 *       <b>Motivation: </b>The number of fields is not constant across calendar systems.
 *   <li>The range of handled dates has been narrowed from +/- ~300,000,000 years to +/- ~5,000,000
 *       years. In practical terms this should not affect clients. However, it does mean that client
 *       code cannot be guaranteed well-behaved results with dates such as <code>
 *       Date(Long.MIN_VALUE)</code> or <code>Date(Long.MAX_VALUE)</code>. Instead, the <code>
 *       Calendar</code> protected constants should be used. <b>Motivation:</b> With the addition of
 *       the {@link #JULIAN_DAY} field, Julian day numbers must be restricted to a 32-bit <code>int
 *       </code>. This restricts the overall supported range. Furthermore, restricting the supported
 *       range simplifies the computations by removing special case code that was used to
 *       accommodate arithmetic overflow at millis near <code>Long.MIN_VALUE</code> and <code>
 *       Long.MAX_VALUE</code>.
 *   <li>New fields are implemented: {@link #JULIAN_DAY} defines single-field specification of the
 *       date. {@link #MILLISECONDS_IN_DAY} defines a single-field specification of the wall time.
 *       {@link #DOW_LOCAL} and {@link #YEAR_WOY} implement localized day-of-week and week-of-year
 *       behavior.
 *   <li>Subclasses can access protected millisecond constants defined in <code>Calendar</code>.
 *   <li>New API has been added to support calendar-specific subclasses of <code>DateFormat</code>.
 *   <li>Several subclasses have been implemented, representing various international calendar
 *       systems.
 * </ul>
 *
 * <p style="font-size:large;font-weight:bold">Subclass API
 *
 * <p>The original <code>Calendar</code> API was based on the experience of implementing a only a
 * single subclass, <code>GregorianCalendar</code>. As a result, all of the subclassing kinks had
 * not been worked out. The new subclassing API has been refined based on several implemented
 * subclasses. This includes methods that must be overridden and methods for subclasses to call.
 * Subclasses no longer have direct access to <code>fields</code> and <code>stamp</code>. Instead,
 * they have new API to access these. Subclasses are able to allocate the <code>fields</code> array
 * through a protected framework method; this allows subclasses to specify additional fields.
 *
 * <p>More functionality has been moved into the base class. The base class now contains much of the
 * computational machinery to support the Gregorian calendar. This is based on two things: (1) Many
 * calendars are based on the Gregorian calendar (such as the Buddhist and Japanese imperial
 * calendars). (2) <em>All</em> calendars require basic Gregorian support in order to handle
 * timezone computations.
 *
 * <p>Common computations have been moved into <code>Calendar</code>. Subclasses no longer compute
 * the week related fields and the time related fields. These are commonly handled for all calendars
 * by the base class.
 *
 * <p><b>Subclass computation of time {@code =&gt;} fields</b>
 *
 * <p>The {@link #ERA}, {@link #YEAR}, {@link #EXTENDED_YEAR}, {@link #MONTH}, {@link
 * #DAY_OF_MONTH}, and {@link #DAY_OF_YEAR} fields are computed by the subclass, based on the Julian
 * day. All other fields are computed by <code>Calendar</code>.
 *
 * <ul>
 *   <li>Subclasses should implement {@link #handleComputeFields} to compute the {@link #ERA},
 *       {@link #YEAR}, {@link #EXTENDED_YEAR}, {@link #MONTH}, {@link #DAY_OF_MONTH}, and {@link
 *       #DAY_OF_YEAR} fields, based on the value of the {@link #JULIAN_DAY} field. If there are
 *       calendar-specific fields not defined by <code>Calendar</code>, they must also be computed.
 *       These are the only fields that the subclass should compute. All other fields are computed
 *       by the base class, so time and week fields behave in a consistent way across all calendars.
 *       The default version of this method in <code>Calendar</code> implements a proleptic
 *       Gregorian calendar. Within this method, subclasses may call <code>getGregorianXxx()</code>
 *       to obtain the Gregorian calendar month, day of month, and extended year for the given date.
 * </ul>
 *
 * <p><b>Subclass computation of fields {@code =&gt;} time</b>
 *
 * <p>The interpretation of most field values is handled entirely by <code>Calendar</code>. <code>
 * Calendar</code> determines which fields are set, which are not, which are set more recently, and
 * so on. In addition, <code>Calendar</code> handles the computation of the time from the time
 * fields and handles the week-related fields. The only thing the subclass must do is determine the
 * extended year, based on the year fields, and then, given an extended year and a month, it must
 * return a Julian day number.
 *
 * <ul>
 *   <li>Subclasses should implement {@link #handleGetExtendedYear} to return the extended year for
 *       this calendar system, based on the {@link #YEAR}, {@link #EXTENDED_YEAR}, and any fields
 *       that the calendar system uses that are larger than a year, such as {@link #ERA}.
 *   <li>Subclasses should implement {@link #handleComputeMonthStart} to return the Julian day
 *       number associated with a month and extended year. This is the Julian day number of the day
 *       before the first day of the month. The month number is zero-based. This computation should
 *       not depend on any field values.
 * </ul>
 *
 * <p><b>Other methods</b>
 *
 * <ul>
 *   <li>Subclasses should implement {@link #handleGetMonthLength} to return the number of days in a
 *       given month of a given extended year. The month number, as always, is zero-based.
 *   <li>Subclasses should implement {@link #handleGetYearLength} to return the number of days in
 *       the given extended year. This method is used by {@code computeWeekFields} to compute the
 *       {@link #WEEK_OF_YEAR} and {@link #YEAR_WOY} fields.
 *   <li>Subclasses should implement {@link #handleGetLimit} to return the protected values of a
 *       field, depending on the value of <code>limitType</code>. This method only needs to handle
 *       the fields {@link #ERA}, {@link #YEAR}, {@link #MONTH}, {@link #WEEK_OF_YEAR}, {@link
 *       #WEEK_OF_MONTH}, {@link #DAY_OF_MONTH}, {@link #DAY_OF_YEAR}, {@link
 *       #DAY_OF_WEEK_IN_MONTH}, {@link #YEAR_WOY}, and {@link #EXTENDED_YEAR}. Other fields are
 *       invariant (with respect to calendar system) and are handled by the base class.
 *   <li>Optionally, subclasses may override {@link #validateField} to check any subclass-specific
 *       fields. If the field's value is out of range, the method should throw an <code>
 *       IllegalArgumentException</code>. The method may call <code>super.validateField(field)
 *       </code> to handle fields in a generic way, that is, to compare them to the range <code>
 *       getMinimum(field)</code>..<code>getMaximum(field)</code>.
 *   <li>Optionally, subclasses may override {@link #handleCreateFields} to create an <code>int[]
 *       </code> array large enough to hold the calendar's fields. This is only necessary if the
 *       calendar defines additional fields beyond those defined by <code>Calendar</code>. The
 *       length of the result must be be between the base and maximum field counts.
 *   <li>Optionally, subclasses may override {@link #handleGetDateFormat} to create a <code>
 *       DateFormat</code> appropriate to this calendar. This is only required if a calendar
 *       subclass redefines the use of a field (for example, changes the {@link #ERA} field from a
 *       symbolic field to a numeric one) or defines an additional field.
 *   <li>Optionally, subclasses may override {@link #roll roll} and {@link #add add} to handle
 *       fields that are discontinuous. For example, in the Hebrew calendar the month &quot;Adar
 *       I&quot; only occurs in leap years; in other years the calendar jumps from Shevat (month #4)
 *       to Adar (month #6). The {@link HebrewCalendar#add HebrewCalendar.add} and {@link
 *       HebrewCalendar#roll HebrewCalendar.roll} methods take this into account, so that adding 1
 *       month to Shevat gives the proper result (Adar) in a non-leap year. The protected utility
 *       method {@link #pinField pinField} is often useful when implementing these two methods.
 * </ul>
 *
 * <p style="font-size:large;font-weight:bold">Normalized behavior
 *
 * <p>The behavior of certain fields has been made consistent across all calendar systems and
 * implemented in <code>Calendar</code>.
 *
 * <ul>
 *   <li>Time is normalized. Even though some calendar systems transition between days at sunset or
 *       at other times, all ICU4J calendars transition between days at <em>local zone
 *       midnight</em>. This allows ICU4J to centralize the time computations in <code>Calendar
 *       </code> and to maintain basic correspondences between calendar systems. Affected fields:
 *       {@link #AM_PM}, {@link #HOUR}, {@link #HOUR_OF_DAY}, {@link #MINUTE}, {@link #SECOND},
 *       {@link #MILLISECOND}, {@link #ZONE_OFFSET}, and {@link #DST_OFFSET}.
 *   <li>DST behavior is normalized. Daylight savings time behavior is computed the same for all
 *       calendar systems, and depends on the value of several <code>GregorianCalendar</code>
 *       fields: the {@link #YEAR}, {@link #MONTH}, and {@link #DAY_OF_MONTH}. As a result, <code>
 *       Calendar</code> always computes these fields, even for non-Gregorian calendar systems.
 *       These fields are available to subclasses.
 *   <li>Weeks are normalized. Although locales define the week differently, in terms of the day on
 *       which it starts, and the designation of week number one of a month or year, they all use a
 *       common mechanism. Furthermore, the day of the week has a simple and consistent definition
 *       throughout history. For example, although the Gregorian calendar introduced a discontinuity
 *       when first instituted, the day of week was not disrupted. For this reason, the fields
 *       {@link #DAY_OF_WEEK}, <code>WEEK_OF_YEAR,
 *     WEEK_OF_MONTH</code>, {@link #DAY_OF_WEEK_IN_MONTH}, {@link #DOW_LOCAL}, {@link #YEAR_WOY}
 *       are all computed in a consistent way in the base class, based on the {@link
 *       #EXTENDED_YEAR}, {@link #DAY_OF_YEAR}, {@link #MONTH}, and {@link #DAY_OF_MONTH}, which are
 *       computed by the subclass.
 * </ul>
 *
 * <p style="font-size:large;font-weight:bold">Supported range
 *
 * <p>The allowable range of <code>Calendar</code> has been narrowed. <code>GregorianCalendar</code>
 * used to attempt to support the range of dates with millisecond values from <code>Long.MIN_VALUE
 * </code> to <code>Long.MAX_VALUE</code>. This introduced awkward constructions (hacks) which
 * slowed down performance. It also introduced non-uniform behavior at the boundaries. The new
 * <code>Calendar</code> protocol specifies the maximum range of supportable dates as those having
 * Julian day numbers of <code>-0x7F000000</code> to <code>+0x7F000000</code>. This corresponds to
 * years from ~5,800,000 BCE to ~5,800,000 CE. Programmers should use the protected constants in
 * <code>Calendar</code> to specify an extremely early or extremely late date.
 *
 * <p style="font-size:large;font-weight:bold">General notes
 *
 * <ul>
 *   <li>Calendars implementations are <em>proleptic</em>. For example, even though the Gregorian
 *       calendar was not instituted until the 16th century, the <code>GregorianCalendar</code>
 *       class supports dates before the historical onset of the calendar by extending the calendar
 *       system backward in time. Similarly, the <code>HebrewCalendar</code> extends backward before
 *       the start of its epoch into zero and negative years. Subclasses do not throw exceptions
 *       because a date precedes the historical start of a calendar system. Instead, they implement
 *       {@link #handleGetLimit} to return appropriate limits on {@link #YEAR}, {@link #ERA}, etc.
 *       fields. Then, if the calendar is set to not be lenient, out-of-range field values will
 *       trigger an exception.
 *   <li>Calendar system subclasses compute a <em>extended year</em>. This differs from the {@link
 *       #YEAR} field in that it ranges over all integer values, including zero and negative values,
 *       and it encapsulates the information of the {@link #YEAR} field and all larger fields. Thus,
 *       for the Gregorian calendar, the {@link #EXTENDED_YEAR} is computed as <code>
 *       ERA==AD ? YEAR : 1-YEAR</code>. Another example is the Mayan long count, which has years (
 *       <code>KUN</code>) and nested cycles of years (<code>KATUN</code> and <code>BAKTUN</code>).
 *       The Mayan {@link #EXTENDED_YEAR} is computed as <code>TUN + 20 * (KATUN
 *     + 20 * BAKTUN)</code>. The <code>Calendar</code> base class uses the {@link #EXTENDED_YEAR}
 *       field to compute the week-related fields.
 * </ul>
 *
 * @see Date
 * @see GregorianCalendar
 * @see TimeZone
 * @see DateFormat
 * @author Mark Davis, Deborah Goldsmith, Chen-Lieh Huang, Alan Liu, Laura Werner
 * @stable ICU 2.0
 */
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {

    // Data flow in Calendar
    // ---------------------

    // The current time is represented in two ways by Calendar: as UTC
    // milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local
    // fields such as MONTH, HOUR, AM_PM, etc.  It is possible to compute the
    // millis from the fields, and vice versa.  The data needed to do this
    // conversion is encapsulated by a TimeZone object owned by the Calendar.
    // The data provided by the TimeZone object may also be overridden if the
    // user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class
    // keeps track of what information was most recently set by the caller, and
    // uses that to compute any other information as needed.

    // If the user sets the fields using set(), the data flow is as follows.
    // This is implemented by the Calendar subclass's computeTime() method.
    // During this process, certain fields may be ignored.  The disambiguation
    // algorithm for resolving which fields to pay attention to is described
    // above.

    //   local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
    //           |
    //           | Using Calendar-specific algorithm
    //           V
    //   local standard millis
    //           |
    //           | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET
    //           V
    //   UTC millis (in time data member)

    // If the user sets the UTC millis using setTime(), the data flow is as
    // follows.  This is implemented by the Calendar subclass's computeFields()
    // method.

    //   UTC millis (in time data member)
    //           |
    //           | Using TimeZone getOffset()
    //           V
    //   local standard millis
    //           |
    //           | Using Calendar-specific algorithm
    //           V
    //   local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)

    // In general, a round trip from fields, through local and UTC millis, and
    // back out to fields is made when necessary.  This is implemented by the
    // complete() method.  Resolving a partial set of fields into a UTC millis
    // value allows all remaining fields to be generated from that value.  If
    // the Calendar is lenient, the fields are also renormalized to standard
    // ranges when they are regenerated.

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the era, e.g., AD or BC in
     * the Julian calendar. This is a calendar-specific value; see subclass documentation.
     *
     * @see GregorianCalendar#AD
     * @see GregorianCalendar#BC
     * @stable ICU 2.0
     */
    public static final int ERA = 0;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the year. This is a
     * calendar-specific value; see subclass documentation.
     *
     * @stable ICU 2.0
     */
    public static final int YEAR = 1;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the month. This is a
     * calendar-specific value. The first month of the year is <code>JANUARY</code>; the last
     * depends on the number of months in a year.
     *
     * @see #JANUARY
     * @see #FEBRUARY
     * @see #MARCH
     * @see #APRIL
     * @see #MAY
     * @see #JUNE
     * @see #JULY
     * @see #AUGUST
     * @see #SEPTEMBER
     * @see #OCTOBER
     * @see #NOVEMBER
     * @see #DECEMBER
     * @see #UNDECIMBER
     * @stable ICU 2.0
     */
    public static final int MONTH = 2;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the week number within the
     * current year. The first week of the year, as defined by {@link #getFirstDayOfWeek()} and
     * {@link #getMinimalDaysInFirstWeek()}, has value 1. Subclasses define the value of {@link
     * #WEEK_OF_YEAR} for days before the first week of the year.
     *
     * @see #getFirstDayOfWeek
     * @see #getMinimalDaysInFirstWeek
     * @stable ICU 2.0
     */
    public static final int WEEK_OF_YEAR = 3;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the week number within the
     * current month. The first week of the month, as defined by {@link #getFirstDayOfWeek()} and
     * {@link #getMinimalDaysInFirstWeek()}, has value 1. Subclasses define the value of {@link
     * #WEEK_OF_MONTH} for days before the first week of the month.
     *
     * @see #getFirstDayOfWeek
     * @see #getMinimalDaysInFirstWeek
     * @stable ICU 2.0
     */
    public static final int WEEK_OF_MONTH = 4;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the day of the month. This
     * is a synonym for {@link #DAY_OF_MONTH}. The first day of the month has value 1.
     *
     * @see #DAY_OF_MONTH
     * @stable ICU 2.0
     */
    public static final int DATE = 5;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the day of the month. This
     * is a synonym for {@link #DATE}. The first day of the month has value 1.
     *
     * @see #DATE
     * @stable ICU 2.0
     */
    public static final int DAY_OF_MONTH = 5;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the day number within the
     * current year. The first day of the year has value 1.
     *
     * @stable ICU 2.0
     */
    public static final int DAY_OF_YEAR = 6;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the day of the week. This
     * field takes values {@link #SUNDAY}, {@link #MONDAY}, {@link #TUESDAY}, {@link #WEDNESDAY},
     * {@link #THURSDAY}, {@link #FRIDAY}, and {@link #SATURDAY}.
     *
     * @see #SUNDAY
     * @see #MONDAY
     * @see #TUESDAY
     * @see #WEDNESDAY
     * @see #THURSDAY
     * @see #FRIDAY
     * @see #SATURDAY
     * @stable ICU 2.0
     */
    public static final int DAY_OF_WEEK = 7;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the ordinal number of the
     * day of the week within the current month. Together with the {@link #DAY_OF_WEEK} field, this
     * uniquely specifies a day within a month. Unlike {@link #WEEK_OF_MONTH} and {@link
     * #WEEK_OF_YEAR}, this field's value does <em>not</em> depend on {@link #getFirstDayOfWeek()}
     * or {@link #getMinimalDaysInFirstWeek()}. <code>DAY_OF_MONTH 1</code> through <code>7</code>
     * always correspond to <code>DAY_OF_WEEK_IN_MONTH
     * 1</code>; <code>8</code> through <code>15</code> correspond to <code>DAY_OF_WEEK_IN_MONTH 2
     * </code>, and so on. <code>DAY_OF_WEEK_IN_MONTH 0</code> indicates the week before <code>
     * DAY_OF_WEEK_IN_MONTH 1</code>. Negative values count back from the end of the month, so the
     * last Sunday of a month is specified as <code>DAY_OF_WEEK = SUNDAY, DAY_OF_WEEK_IN_MONTH = -1
     * </code>. Because negative values count backward they will usually be aligned differently
     * within the month than positive values. For example, if a month has 31 days, <code>
     * DAY_OF_WEEK_IN_MONTH -1</code> will overlap <code>DAY_OF_WEEK_IN_MONTH 5</code> and the end
     * of <code>4</code>.
     *
     * @see #DAY_OF_WEEK
     * @see #WEEK_OF_MONTH
     * @stable ICU 2.0
     */
    public static final int DAY_OF_WEEK_IN_MONTH = 8;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating whether the <code>HOUR
     * </code> is before or after noon. E.g., at 10:04:15.250 PM the <code>AM_PM</code> is <code>PM
     * </code>.
     *
     * @see #AM
     * @see #PM
     * @see #HOUR
     * @stable ICU 2.0
     */
    public static final int AM_PM = 9;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the hour of the morning or
     * afternoon. <code>HOUR</code> is used for the 12-hour clock. E.g., at 10:04:15.250 PM the
     * <code>HOUR</code> is 10.
     *
     * @see #AM_PM
     * @see #HOUR_OF_DAY
     * @stable ICU 2.0
     */
    public static final int HOUR = 10;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the hour of the day. <code>
     * HOUR_OF_DAY</code> is used for the 24-hour clock. E.g., at 10:04:15.250 PM the <code>
     * HOUR_OF_DAY</code> is 22.
     *
     * @see #HOUR
     * @stable ICU 2.0
     */
    public static final int HOUR_OF_DAY = 11;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the minute within the hour.
     * E.g., at 10:04:15.250 PM the <code>MINUTE</code> is 4.
     *
     * @stable ICU 2.0
     */
    public static final int MINUTE = 12;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the second within the
     * minute. E.g., at 10:04:15.250 PM the <code>SECOND</code> is 15.
     *
     * @stable ICU 2.0
     */
    public static final int SECOND = 13;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the millisecond within the
     * second. E.g., at 10:04:15.250 PM the <code>MILLISECOND</code> is 250.
     *
     * @stable ICU 2.0
     */
    public static final int MILLISECOND = 14;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the raw offset from GMT in
     * milliseconds.
     *
     * @stable ICU 2.0
     */
    public static final int ZONE_OFFSET = 15;

    /**
     * Field number for <code>get</code> and <code>set</code> indicating the daylight savings offset
     * in milliseconds.
     *
     * @stable ICU 2.0
     */
    public static final int DST_OFFSET = 16;

    /**
     * {@icu} Field number for <code>get()</code> and <code>set()</code> indicating the extended
     * year corresponding to the {@link #WEEK_OF_YEAR} field. This may be one greater or less than
     * the value of {@link #EXTENDED_YEAR}.
     *
     * @stable ICU 2.0
     */
    public static final int YEAR_WOY = 17;

    /**
     * {@icu} Field number for <code>get()</code> and <code>set()</code> indicating the localized
     * day of week. This will be a value from 1 to 7 inclusive, with 1 being the localized first day
     * of the week.
     *
     * @stable ICU 2.0
     */
    public static final int DOW_LOCAL = 18;

    /**
     * {@icu} Field number for <code>get()</code> and <code>set()</code> indicating the extended
     * year. This is a single number designating the year of this calendar system, encompassing all
     * supra-year fields. For example, for the Julian calendar system, year numbers are positive,
     * with an era of BCE or CE. An extended year value for the Julian calendar system assigns
     * positive values to CE years and negative values to BCE years, with 1 BCE being year 0.
     *
     * @stable ICU 2.0
     */
    public static final int EXTENDED_YEAR = 19;

    /**
     * {@icu} Field number for <code>get()</code> and <code>set()</code> indicating the modified
     * Julian day number. This is different from the conventional Julian day number in two regards.
     * First, it demarcates days at local zone midnight, rather than noon GMT. Second, it is a local
     * number; that is, it depends on the local time zone. It can be thought of as a single number
     * that encompasses all the date-related fields.
     *
     * @stable ICU 2.0
     */
    public static final int JULIAN_DAY = 20;

    /**
     * {@icu} Field number for <code>get()</code> and <code>set()</code> indicating the milliseconds
     * in the day. This ranges from 0 to 23:59:59.999 (regardless of DST). This field behaves
     * <em>exactly</em> like a composite of all time-related fields, not including the zone fields.
     * As such, it also reflects discontinuities of those fields on DST transition days. On a day of
     * DST onset, it will jump forward. On a day of DST cessation, it will jump backward. This
     * reflects the fact that is must be combined with the DST_OFFSET field to obtain a unique local
     * time value.
     *
     * @stable ICU 2.0
     */
    public static final int MILLISECONDS_IN_DAY = 21;

    /**
     * {@icu} Field indicating whether or not the current month is a leap month. Should have a value
     * of 0 for non-leap months, and 1 for leap months.
     *
     * @stable ICU 4.4
     */
    public static final int IS_LEAP_MONTH = 22;

    /**
     * {@icu} Field indicating the month. This is a calendar-specific value. Differ from MONTH, this
     * value is continuous and unique within a year and range from 0 to 11 or 0 to 12 depending on
     * how many months in a year, the calendar system has leap month or not, and in leap year or
     * not. It is the ordinal position of that month in the corresponding year of the calendar. For
     * Chinese, Dangi, and Hebrew calendar, the range is 0 to 11 in non-leap years and 0 to 12 in
     * leap years. For Coptic and Ethiopian calendar, the range is always 0 to 12. For other
     * calendars supported by ICU now, the range is 0 to 11. When the number of months in a year of
     * the identified calendar is variable, a different ORDINAL_MONTH value can be used for dates
     * that are part of the same named month in different years. For example, in the Hebrew
     * calendar, "1 Nisan 5781" is associated with ORDINAL_MONTH value 6 while "1 Nisan 5782" is
     * associated with ORDINAL_MONTH value 7 because 5782 is a leap year and Nisan follows the
     * insertion of Adar I. In Chinese calendar, "Year 4664 Month 6 Day 2" is associated with
     * ORDINAL_MONTH value 5 while "Year 4665 Month 6 Day 2" is associated with ORDINAL_MONTH value
     * 6 because 4665 is a leap year and there is an extra "Leap Month 5" which associated with
     * ORDINAL_MONTH value 5 before "Month 6" of year 4664.
     *
     * @stable ICU 74
     */
    public static final int ORDINAL_MONTH = 23;

    /**
     * The number of fields defined by this class. Subclasses may define addition fields starting
     * with this number.
     *
     * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420.
     */
    @Deprecated protected static final int BASE_FIELD_COUNT = 24;

    /**
     * The maximum number of fields possible. Subclasses must not define more total fields than this
     * number.
     *
     * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420.
     */
    @Deprecated protected static final int MAX_FIELD_COUNT = 32;

    /**
     * Value of the <code>DAY_OF_WEEK</code> field indicating Sunday.
     *
     * @stable ICU 2.0
     */
    public static final int SUNDAY = 1;

    /**
     * Value of the <code>DAY_OF_WEEK</code> field indicating Monday.
     *
     * @stable ICU 2.0
     */
    public static final int MONDAY = 2;

    /**
     * Value of the <code>DAY_OF_WEEK</code> field indicating Tuesday.
     *
     * @stable ICU 2.0
     */
    public static final int TUESDAY = 3;

    /**
     * Value of the <code>DAY_OF_WEEK</code> field indicating Wednesday.
     *
     * @stable ICU 2.0
     */
    public static final int WEDNESDAY = 4;

    /**
     * Value of the <code>DAY_OF_WEEK</code> field indicating Thursday.
     *
     * @stable ICU 2.0
     */
    public static final int THURSDAY = 5;

    /**
     * Value of the <code>DAY_OF_WEEK</code> field indicating Friday.
     *
     * @stable ICU 2.0
     */
    public static final int FRIDAY = 6;

    /**
     * Value of the <code>DAY_OF_WEEK</code> field indicating Saturday.
     *
     * @stable ICU 2.0
     */
    public static final int SATURDAY = 7;

    /**
     * Value of the <code>MONTH</code> field indicating the first month of the year.
     *
     * @stable ICU 2.0
     */
    public static final int JANUARY = 0;

    /**
     * Value of the <code>MONTH</code> field indicating the second month of the year.
     *
     * @stable ICU 2.0
     */
    public static final int FEBRUARY = 1;

    /**
     * Value of the <code>MONTH</code> field indicating the third month of the year.
     *
     * @stable ICU 2.0
     */
    public static final int MARCH = 2;

    /**
     * Value of the <code>MONTH</code> field indicating the fourth month of the year.
     *
     * @stable ICU 2.0
     */
    public static final int APRIL = 3;

    /**
     * Value of the <code>MONTH</code> field indicating the fifth month of the year.
     *
     * @stable ICU 2.0
     */
    public static final int MAY = 4;

    /**
     * Value of the <code>MONTH</code> field indicating the sixth month of the year.
     *
     * @stable ICU 2.0
     */
    public static final int JUNE = 5;

    /**
     * Value of the <code>MONTH</code> field indicating the seventh month of the year.
     *
     * @stable ICU 2.0
     */
    public static final int JULY = 6;

    /**
     * Value of the <code>MONTH</code> field indicating the eighth month of the year.
     *
     * @stable ICU 2.0
     */
    public static final int AUGUST = 7;

    /**
     * Value of the <code>MONTH</code> field indicating the ninth month of the year.
     *
     * @stable ICU 2.0
     */
    public static final int SEPTEMBER = 8;

    /**
     * Value of the <code>MONTH</code> field indicating the tenth month of the year.
     *
     * @stable ICU 2.0
     */
    public static final int OCTOBER = 9;

    /**
     * Value of the <code>MONTH</code> field indicating the eleventh month of the year.
     *
     * @stable ICU 2.0
     */
    public static final int NOVEMBER = 10;

    /**
     * Value of the <code>MONTH</code> field indicating the twelfth month of the year.
     *
     * @stable ICU 2.0
     */
    public static final int DECEMBER = 11;

    /**
     * Value of the <code>MONTH</code> field indicating the thirteenth month of the year. Although
     * {@link GregorianCalendar} does not use this value, lunar calendars do.
     *
     * @stable ICU 2.0
     */
    public static final int UNDECIMBER = 12;

    /**
     * Value of the <code>AM_PM</code> field indicating the period of the day from midnight to just
     * before noon.
     *
     * @stable ICU 2.0
     */
    public static final int AM = 0;

    /**
     * Value of the <code>AM_PM</code> field indicating the period of the day from noon to just
     * before midnight.
     *
     * @stable ICU 2.0
     */
    public static final int PM = 1;

    /**
     * {@icu} Value returned by getDayOfWeekType(int dayOfWeek) to indicate a weekday.
     *
     * @see #WEEKEND
     * @see #WEEKEND_ONSET
     * @see #WEEKEND_CEASE
     * @see #getDayOfWeekType
     * @deprecated ICU 54 use {@link #getWeekDataForRegion(String)}, {@link #getWeekData()}, {@link
     *     #setWeekData(WeekData)}
     */
    @Deprecated public static final int WEEKDAY = 0;

    /**
     * {@icu} Value returned by getDayOfWeekType(int dayOfWeek) to indicate a weekend day.
     *
     * @see #WEEKDAY
     * @see #WEEKEND_ONSET
     * @see #WEEKEND_CEASE
     * @see #getDayOfWeekType
     * @deprecated ICU 54 use {@link #getWeekDataForRegion(String)}, {@link #getWeekData()}, {@link
     *     #setWeekData(WeekData)}
     */
    @Deprecated public static final int WEEKEND = 1;

    /**
     * {@icu} Value returned by getDayOfWeekType(int dayOfWeek) to indicate a day that starts as a
     * weekday and transitions to the weekend. Call getWeekendTransition() to get the point of
     * transition.
     *
     * @see #WEEKDAY
     * @see #WEEKEND
     * @see #WEEKEND_CEASE
     * @see #getDayOfWeekType
     * @deprecated ICU 54 use {@link #getWeekDataForRegion(String)}, {@link #getWeekData()}, {@link
     *     #setWeekData(WeekData)}
     */
    @Deprecated public static final int WEEKEND_ONSET = 2;

    /**
     * {@icu} Value returned by getDayOfWeekType(int dayOfWeek) to indicate a day that starts as the
     * weekend and transitions to a weekday. Call getWeekendTransition() to get the point of
     * transition.
     *
     * @see #WEEKDAY
     * @see #WEEKEND
     * @see #WEEKEND_ONSET
     * @see #getDayOfWeekType
     * @deprecated ICU 54 use {@link #getWeekDataForRegion(String)}, {@link #getWeekData()}, {@link
     *     #setWeekData(WeekData)}
     */
    @Deprecated public static final int WEEKEND_CEASE = 3;

    /**
     * {@icu}Option used by {@link #setRepeatedWallTimeOption(int)} and {@link
     * #setSkippedWallTimeOption(int)} specifying an ambiguous wall time to be interpreted as the
     * latest.
     *
     * @see #setRepeatedWallTimeOption(int)
     * @see #getRepeatedWallTimeOption()
     * @see #setSkippedWallTimeOption(int)
     * @see #getSkippedWallTimeOption()
     * @stable ICU 49
     */
    public static final int WALLTIME_LAST = 0;

    /**
     * {@icu}Option used by {@link #setRepeatedWallTimeOption(int)} and {@link
     * #setSkippedWallTimeOption(int)} specifying an ambiguous wall time to be interpreted as the
     * earliest.
     *
     * @see #setRepeatedWallTimeOption(int)
     * @see #getRepeatedWallTimeOption()
     * @see #setSkippedWallTimeOption(int)
     * @see #getSkippedWallTimeOption()
     * @stable ICU 49
     */
    public static final int WALLTIME_FIRST = 1;

    /**
     * {@icu}Option used by {@link #setSkippedWallTimeOption(int)} specifying an ambiguous wall time
     * to be interpreted as the next valid wall time.
     *
     * @see #setSkippedWallTimeOption(int)
     * @see #getSkippedWallTimeOption()
     * @stable ICU 49
     */
    public static final int WALLTIME_NEXT_VALID = 2;

    /**
     * The number of milliseconds in one second.
     *
     * @stable ICU 2.0
     */
    protected static final int ONE_SECOND = 1000;

    /**
     * The number of milliseconds in one minute.
     *
     * @stable ICU 2.0
     */
    protected static final int ONE_MINUTE = 60 * ONE_SECOND;

    /**
     * The number of milliseconds in one hour.
     *
     * @stable ICU 2.0
     */
    protected static final int ONE_HOUR = 60 * ONE_MINUTE;

    /**
     * The number of milliseconds in one day. Although ONE_DAY and ONE_WEEK can fit into ints, they
     * must be longs in order to prevent arithmetic overflow when performing (bug 4173516).
     *
     * @stable ICU 2.0
     */
    protected static final long ONE_DAY = 24 * ONE_HOUR;

    /**
     * The number of milliseconds in one week. Although ONE_DAY and ONE_WEEK can fit into ints, they
     * must be longs in order to prevent arithmetic overflow when performing (bug 4173516).
     *
     * @stable ICU 2.0
     */
    protected static final long ONE_WEEK = 7 * ONE_DAY;

    /**
     * The Julian day of the Gregorian epoch, that is, January 1, 1 on the Gregorian calendar.
     *
     * @stable ICU 2.0
     */
    protected static final int JAN_1_1_JULIAN_DAY = 1721426;

    /**
     * The Julian day of the epoch, that is, January 1, 1970 on the Gregorian calendar.
     *
     * @stable ICU 2.0
     */
    protected static final int EPOCH_JULIAN_DAY = 2440588;

    /**
     * The minimum supported Julian day. This value is equivalent to {@link #MIN_MILLIS} and {@link
     * #MIN_DATE}.
     *
     * @see #JULIAN_DAY
     * @stable ICU 2.0
     */
    protected static final int MIN_JULIAN = -0x7F000000;

    /**
     * The minimum supported epoch milliseconds. This value is equivalent to {@link #MIN_JULIAN} and
     * {@link #MIN_DATE}.
     *
     * @stable ICU 2.0
     */
    protected static final long MIN_MILLIS = -184303902528000000L;

    // Get around bug in jikes 1.12 for now.  Later, use:
    // protected static final long MIN_MILLIS = (MIN_JULIAN - EPOCH_JULIAN_DAY) * ONE_DAY;

    /**
     * The minimum supported <code>Date</code>. This value is equivalent to {@link #MIN_JULIAN} and
     * {@link #MIN_MILLIS}.
     *
     * @stable ICU 2.0
     */
    protected static final Date MIN_DATE = new Date(MIN_MILLIS);

    /**
     * The maximum supported Julian day. This value is equivalent to {@link #MAX_MILLIS} and {@link
     * #MAX_DATE}.
     *
     * @see #JULIAN_DAY
     * @stable ICU 2.0
     */
    protected static final int MAX_JULIAN = +0x7F000000;

    /**
     * The maximum supported epoch milliseconds. This value is equivalent to {@link #MAX_JULIAN} and
     * {@link #MAX_DATE}.
     *
     * @stable ICU 2.0
     */
    protected static final long MAX_MILLIS = (MAX_JULIAN - EPOCH_JULIAN_DAY) * ONE_DAY;

    /**
     * The maximum supported <code>Date</code>. This value is equivalent to {@link #MAX_JULIAN} and
     * {@link #MAX_MILLIS}.
     *
     * @stable ICU 2.0
     */
    protected static final Date MAX_DATE = new Date(MAX_MILLIS);

    /** The maximum supported hours for millisecond calculations */
    private static final int MAX_HOURS = 548;

    // Internal notes:
    // Calendar contains two kinds of time representations: current "time" in
    // milliseconds, and a set of time "fields" representing the current time.
    // The two representations are usually in sync, but can get out of sync
    // as follows.
    // 1. Initially, no fields are set, and the time is invalid.
    // 2. If the time is set, all fields are computed and in sync.
    // 3. If a single field is set, the time is invalid.
    // Recomputation of the time and fields happens when the object needs
    // to return a result to the user, or use a result for a computation.

    /**
     * The field values for the currently set time for this calendar. This is an array of at least
     * {@link #BASE_FIELD_COUNT} integers.
     *
     * @see #handleCreateFields
     * @serial
     */
    private transient int fields[];

    /**
     * Pseudo-time-stamps which specify when each field was set. There are two special values, UNSET
     * and INTERNALLY_SET. Values from MINIMUM_USER_SET to STAMP_MAX are legal user set values.
     */
    private transient byte stamp[];

    /**
     * The currently set time for this calendar, expressed in milliseconds after January 1, 1970,
     * 0:00:00 GMT.
     *
     * @serial
     */
    private long time;

    /**
     * True if then the value of <code>time</code> is valid. The time is made invalid by a change to
     * an item of <code>field[]</code>.
     *
     * @see #time
     * @serial
     */
    private transient boolean isTimeSet;

    /**
     * True if <code>fields[]</code> are in sync with the currently set time. If false, then the
     * next attempt to get the value of a field will force a recomputation of all fields from the
     * current value of <code>time</code>.
     *
     * @serial
     */
    private transient boolean areFieldsSet;

    /**
     * True if all fields have been set. This is only false in a few situations: In a newly created,
     * partially constructed object. After a call to clear(). In an object just read from a stream
     * using readObject(). Once computeFields() has been called this is set to true and stays true
     * until one of the above situations recurs.
     *
     * @serial
     */
    private transient boolean areAllFieldsSet;

    /**
     * True if all fields have been virtually set, but have not yet been computed. This occurs only
     * in setTimeInMillis(), or after readObject(). A calendar set to this state will compute all
     * fields from the time if it becomes necessary, but otherwise will delay such computation.
     */
    private transient boolean areFieldsVirtuallySet;

    /**
     * True if this calendar allows out-of-range field values during computation of <code>time
     * </code> from <code>fields[]</code>.
     *
     * @see #setLenient
     * @serial
     */
    private boolean lenient = true;

    /**
     * The {@link TimeZone} used by this calendar. {@link Calendar} uses the time zone data to
     * translate between local and GMT time.
     *
     * @serial
     */
    private TimeZone zone;

    /**
     * The first day of the week, with possible values {@link #SUNDAY}, {@link #MONDAY}, etc. This
     * is a locale-dependent value.
     *
     * @serial
     */
    private int firstDayOfWeek;

    /**
     * The number of days required for the first week in a month or year, with possible values from
     * 1 to 7. This is a locale-dependent value.
     *
     * @serial
     */
    private int minimalDaysInFirstWeek;

    /**
     * First day of the weekend in this calendar's locale. Must be in the range SUNDAY...SATURDAY
     * (1..7). The weekend starts at weekendOnsetMillis milliseconds after midnight on that day of
     * the week. This value is taken from locale resource data.
     */
    private int weekendOnset;

    /**
     * Milliseconds after midnight at which the weekend starts on the day of the week weekendOnset.
     * Times that are greater than or equal to weekendOnsetMillis are considered part of the
     * weekend. Must be in the range 0..24*60*60*1000-1. This value is taken from locale resource
     * data.
     */
    private int weekendOnsetMillis;

    /**
     * Day of the week when the weekend stops in this calendar's locale. Must be in the range
     * SUNDAY...SATURDAY (1..7). The weekend stops at weekendCeaseMillis milliseconds after midnight
     * on that day of the week. This value is taken from locale resource data.
     */
    private int weekendCease;

    /**
     * Milliseconds after midnight at which the weekend stops on the day of the week weekendCease.
     * Times that are greater than or equal to weekendCeaseMillis are considered not to be the
     * weekend. Must be in the range 0..24*60*60*1000-1. This value is taken from locale resource
     * data.
     */
    private int weekendCeaseMillis;

    /** Option used when the specified wall time occurs multiple times. */
    private int repeatedWallTime = WALLTIME_LAST;

    /** Option used when the specified wall time does not exist. */
    private int skippedWallTime = WALLTIME_LAST;

    /**
     * Value of the time stamp <code>stamp[]</code> indicating that a field has not been set since
     * the last call to <code>clear()</code>.
     *
     * @see #INTERNALLY_SET
     * @see #MINIMUM_USER_STAMP
     * @stable ICU 2.0
     */
    protected static final int UNSET = 0;

    /**
     * Value of the time stamp <code>stamp[]</code> indicating that a field has been set via
     * computations from the time or from other fields.
     *
     * @see #UNSET
     * @see #MINIMUM_USER_STAMP
     * @stable ICU 2.0
     */
    protected static final int INTERNALLY_SET = 1;

    /**
     * If the time stamp <code>stamp[]</code> has a value greater than or equal to <code>
     * MINIMUM_USER_SET</code> then it has been set by the user via a call to <code>set()</code>.
     *
     * @see #UNSET
     * @see #INTERNALLY_SET
     * @stable ICU 2.0
     */
    protected static final int MINIMUM_USER_STAMP = 2;

    /**
     * The next available value for <code>stamp[]</code>, an internal array.
     *
     * @serial
     */
    private transient byte nextStamp = MINIMUM_USER_STAMP;

    /* Max value for stamp allowable before recalculation */
    private static byte STAMP_MAX = Byte.MAX_VALUE;

    // the internal serial version which says which version was written
    // - 0 (default) for version up to JDK 1.1.5
    // - 1 for version from JDK 1.1.6, which writes a correct 'time' value
    //     as well as compatible values for other fields.  This is a
    //     transitional format.
    // - 2 (not implemented yet) a future version, in which fields[],
    //     areFieldsSet, and isTimeSet become transient, and isSet[] is
    //     removed. In JDK 1.1.6 we write a format compatible with version 2.
    // static final int        currentSerialVersion = 1;

    /**
     * The version of the serialized data on the stream. Possible values:
     *
     * <dl>
     *   <dt><b>0</b> or not present on stream
     *   <dd>JDK 1.1.5 or earlier.
     *   <dt><b>1</b>
     *   <dd>JDK 1.1.6 or later. Writes a correct 'time' value as well as compatible values for
     *       other fields. This is a transitional format.
     * </dl>
     *
     * When streaming out this class, the most recent format and the highest allowable <code>
     * serialVersionOnStream</code> is written.
     *
     * @serial
     * @since JDK1.1.6
     */
    // private int             serialVersionOnStream = currentSerialVersion;

    // Proclaim serialization compatibility with JDK 1.1
    // static final long       serialVersionUID = -1807547505821590642L;

    // haven't been compatible for awhile, no longer try
    // jdk1.4.2 serialver
    private static final long serialVersionUID = 6222646104888790989L;

    /**
     * Bitmask for internalSet() defining which fields may legally be set by subclasses. Any attempt
     * to set a field not in this bitmask results in an exception, because such fields must be set
     * by the base class.
     */
    private transient int internalSetMask;

    /**
     * The Gregorian year, as computed by computeGregorianFields() and returned by
     * getGregorianYear().
     */
    private transient int gregorianYear;

    /**
     * The Gregorian month, as computed by computeGregorianFields() and returned by
     * getGregorianMonth().
     */
    private transient int gregorianMonth;

    /**
     * The Gregorian day of the year, as computed by computeGregorianFields() and returned by
     * getGregorianDayOfYear().
     */
    private transient int gregorianDayOfYear;

    /**
     * The Gregorian day of the month, as computed by computeGregorianFields() and returned by
     * getGregorianDayOfMonth().
     */
    private transient int gregorianDayOfMonth;

    /**
     * Constructs a Calendar with the default time zone and the default <code>FORMAT</code> locale.
     *
     * @see TimeZone#getDefault
     * @see Category#FORMAT
     * @stable ICU 2.0
     */
    protected Calendar() {
        this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
    }

    /**
     * Constructs a calendar with the specified time zone and locale.
     *
     * @param zone the time zone to use
     * @param aLocale the locale for the week data
     * @stable ICU 2.0
     */
    protected Calendar(TimeZone zone, Locale aLocale) {
        this(zone, ULocale.forLocale(aLocale));
    }

    /**
     * Constructs a calendar with the specified time zone and locale.
     *
     * @param zone the time zone to use
     * @param locale the ulocale for the week data
     * @stable ICU 3.2
     */
    protected Calendar(TimeZone zone, ULocale locale) {
        this.zone = zone;

        // week data
        setWeekData(getRegionForCalendar(locale));

        // Check if the locale has a "fw" u extension and we honor it if present.
        String fw = locale.getKeywordValue("fw");
        if (fw != null) {
            int fwOverride;
            switch (fw) {
                case "sun":
                    fwOverride = SUNDAY;
                    break;
                case "mon":
                    fwOverride = MONDAY;
                    break;
                case "tue":
                    fwOverride = TUESDAY;
                    break;
                case "wed":
                    fwOverride = WEDNESDAY;
                    break;
                case "thu":
                    fwOverride = THURSDAY;
                    break;
                case "fri":
                    fwOverride = FRIDAY;
                    break;
                case "sat":
                    fwOverride = SATURDAY;
                    break;
                default:
                    fwOverride = -1;
            }
            if (fwOverride != -1) {
                setFirstDayOfWeek(fwOverride);
            }
        }

        // set valid/actual locale
        setCalendarLocale(locale);

        initInternal();
    }

    /*
     * Set valid/actual locale to this calendar during initialization.
     *
     * Valid or actual locale does not make much sense for Calendar
     * object. An instance of Calendar is initialized by week data
     * determine by region and calendar type (either region or keyword).
     * Language is not really used for calendar creation.
     */
    private void setCalendarLocale(ULocale locale) {
        ULocale calLocale = locale;

        if (locale.getVariant().length() != 0 || locale.getKeywords() != null) {
            // Construct a ULocale, without variant and keywords (except calendar).
            StringBuilder buf = new StringBuilder();

            buf.append(locale.getLanguage());

            String script = locale.getScript();
            if (script.length() > 0) {
                buf.append("_").append(script);
            }

            String region = locale.getCountry();
            if (region.length() > 0) {
                buf.append("_").append(region);
            }

            String calType = locale.getKeywordValue("calendar");
            if (calType != null) {
                buf.append("@calendar=").append(calType);
            }

            calLocale = new ULocale(buf.toString());
        }

        setLocale(calLocale, calLocale);
    }

    private void recalculateStamp() {
        int index;
        byte currentValue;
        int j, i;

        nextStamp = 1;

        for (j = 0; j < stamp.length; j++) {
            currentValue = STAMP_MAX;
            index = -1;

            for (i = 0; i < stamp.length; i++) {
                if (stamp[i] > nextStamp && stamp[i] < currentValue) {
                    currentValue = stamp[i];
                    index = i;
                }
            }

            if (index >= 0) {
                stamp[index] = ++nextStamp;
            } else {
                break;
            }
        }
        nextStamp++;
    }

    private void initInternal() {
        // Allocate fields through the framework method.  Subclasses
        // may override this to define additional fields.
        fields = handleCreateFields();
        /// CLOVER:OFF
        // todo: fix, difficult to test without subclassing
        if (fields == null || fields.length < BASE_FIELD_COUNT || fields.length > MAX_FIELD_COUNT) {
            throw new IllegalStateException("Invalid fields[]");
        }
        /// CLOVER:ON
        stamp = new byte[fields.length];
        int mask =
                (1 << ERA)
                        | (1 << YEAR)
                        | (1 << MONTH)
                        | (1 << DAY_OF_MONTH)
                        | (1 << DAY_OF_YEAR)
                        | (1 << EXTENDED_YEAR)
                        | (1 << IS_LEAP_MONTH)
                        | (1 << ORDINAL_MONTH);
        for (int i = BASE_FIELD_COUNT; i < fields.length; ++i) {
            mask |= (1 << i);
        }
        internalSetMask = mask;
    }

    /**
     * Returns a calendar using the default time zone and locale.
     *
     * @return a Calendar.
     * @stable ICU 2.0
     */
    public static Calendar getInstance() {
        return getInstanceInternal(null, null);
    }

    /**
     * Returns a calendar using the specified time zone and default locale.
     *
     * @param zone the time zone to use
     * @return a Calendar.
     * @stable ICU 2.0
     */
    public static Calendar getInstance(TimeZone zone) {
        return getInstanceInternal(zone, null);
    }

    /**
     * Returns a calendar using the default time zone and specified locale.
     *
     * @param aLocale the locale for the week data
     * @return a Calendar.
     * @stable ICU 2.0
     */
    public static Calendar getInstance(Locale aLocale) {
        return getInstanceInternal(null, ULocale.forLocale(aLocale));
    }

    /**
     * Returns a calendar using the default time zone and specified locale.
     *
     * @param locale the ulocale for the week data
     * @return a Calendar.
     * @stable ICU 3.2
     */
    public static Calendar getInstance(ULocale locale) {
        return getInstanceInternal(null, locale);
    }

    /**
     * Returns a calendar with the specified time zone and locale.
     *
     * @param zone the time zone to use
     * @param aLocale the locale for the week data
     * @return a Calendar.
     * @stable ICU 2.0
     */
    public static Calendar getInstance(TimeZone zone, Locale aLocale) {
        return getInstanceInternal(zone, ULocale.forLocale(aLocale));
    }

    /**
     * Returns a calendar with the specified time zone and locale.
     *
     * @param zone the time zone to use
     * @param locale the ulocale for the week data
     * @return a Calendar.
     * @stable ICU 3.2
     */
    public static Calendar getInstance(TimeZone zone, ULocale locale) {
        return getInstanceInternal(zone, locale);
    }

    /*
     * All getInstance implementations call this private method to create a new
     * Calendar instance.
     */
    private static Calendar getInstanceInternal(TimeZone tz, ULocale locale) {
        if (locale == null) {
            locale = ULocale.getDefault(Category.FORMAT);
        }
        if (tz == null) {
            tz = TimeZone.forULocaleOrDefault(locale);
        }

        Calendar cal = createInstance(locale);
        cal.setTimeZone(tz);
        cal.setTimeInMillis(System.currentTimeMillis());
        return cal;
    }

    private static String getRegionForCalendar(ULocale loc) {
        String region = ULocale.getRegionForSupplementalData(loc, true);
        if (region.length() == 0) {
            region = "001";
        }
        return region;
    }

    private static CalType getCalendarTypeForLocale(ULocale l) {
        String s = CalendarUtil.getCalendarType(l);
        if (s != null) {
            s = s.toLowerCase(Locale.ENGLISH);
            for (CalType type : CalType.values()) {
                if (s.equals(type.getId())) {
                    return type;
                }
            }
        }
        return null;
    }

    private static Calendar createInstance(ULocale locale) {
        Calendar cal = null;
        TimeZone zone = TimeZone.forULocaleOrDefault(locale);
        CalType calType = getCalendarTypeForLocale(locale);
        if (calType == null) {
            // fallback to Gregorian
            calType = CalType.GREGORIAN;
        }

        switch (calType) {
            case GREGORIAN:
                cal = new GregorianCalendar(zone, locale);
                break;
            case ISO8601:
                // Only differs week numbering rule from Gregorian
                cal = new GregorianCalendar(zone, locale);
                // Based on UTS35 "First Day Overrides"
                // https://unicode.org/reports/tr35/tr35-dates.html#first-day-overrides
                // Only set fw to Monday for ISO8601 if there are no fw nor rg keywords.
                // If there is a fw or rg keywords, the Calendar constructor already set it
                // to the fw value or based on the rg value.
                if (locale.getUnicodeLocaleType("fw") == null
                        && locale.getUnicodeLocaleType("rg") == null) {
                    cal.setFirstDayOfWeek(MONDAY);
                }
                cal.setMinimalDaysInFirstWeek(4);
                break;

            case BUDDHIST:
                cal = new BuddhistCalendar(zone, locale);
                break;
            case CHINESE:
                cal = new ChineseCalendar(zone, locale);
                break;
            case COPTIC:
                cal = new CopticCalendar(zone, locale);
                break;
            case DANGI:
                cal = new DangiCalendar(zone, locale);
                break;
            case ETHIOPIC:
                cal = new EthiopicCalendar(zone, locale);
                break;
            case ETHIOPIC_AMETE_ALEM:
                cal = new EthiopicCalendar(zone, locale);
                ((EthiopicCalendar) cal).setAmeteAlemEra(true);
                break;
            case HEBREW:
                cal = new HebrewCalendar(zone, locale);
                break;
            case INDIAN:
                cal = new IndianCalendar(zone, locale);
                break;
            case ISLAMIC_CIVIL:
            case ISLAMIC_UMALQURA:
            case ISLAMIC_TBLA:
            case ISLAMIC_RGSA:
            case ISLAMIC:
                cal = new IslamicCalendar(zone, locale);
                break;
            case JAPANESE:
                cal = new JapaneseCalendar(zone, locale);
                break;
            case PERSIAN:
                cal = new PersianCalendar(zone, locale);
                break;
            case ROC:
                cal = new TaiwanCalendar(zone, locale);
                break;

            default:
                // we must not get here, because unknown type is mapped to
                // Gregorian at the beginning of this method.
                throw new IllegalArgumentException("Unknown calendar type");
        }

        return cal;
    }

    /**
     * Returns the list of locales for which Calendars are installed.
     *
     * @return the list of locales for which Calendars are installed.
     * @stable ICU 2.0
     */
    public static Locale[] getAvailableLocales() {
        // TODO
        return ICUResourceBundle.getAvailableLocales();
    }

    /**
     * {@icu} Returns the list of locales for which Calendars are installed.
     *
     * @return the list of locales for which Calendars are installed.
     * @draft ICU 3.2 (retain)
     */
    public static ULocale[] getAvailableULocales() {
        // TODO
        return ICUResourceBundle.getAvailableULocales();
    }

    /**
     * {@icu} Given a key and a locale, returns an array of string values in a preferred order that
     * would make a difference. These are all and only those values where the open (creation) of the
     * service with the locale formed from the input locale plus input keyword and that value has
     * different behavior than creation with the input locale alone.
     *
     * @param key one of the keys supported by this service. For now, only "calendar" is supported.
     * @param locale the locale
     * @param commonlyUsed if set to true it will return only commonly used values with the given
     *     locale in preferred order. Otherwise, it will return all the available values for the
     *     locale.
     * @return an array of string values for the given key and the locale.
     * @stable ICU 4.2
     */
    public static final String[] getKeywordValuesForLocale(
            String key, ULocale locale, boolean commonlyUsed) {
        // Resolve region
        String prefRegion = ULocale.getRegionForSupplementalData(locale, true);

        // Read preferred calendar values from supplementalData calendarPreferences
        ArrayList<String> values = new ArrayList<>();

        UResourceBundle rb =
                UResourceBundle.getBundleInstance(
                        ICUData.ICU_BASE_NAME,
                        "supplementalData",
                        ICUResourceBundle.ICU_DATA_CLASS_LOADER);
        UResourceBundle calPref = rb.get("calendarPreferenceData");
        UResourceBundle order = null;
        try {
            order = calPref.get(prefRegion);
        } catch (MissingResourceException mre) {
            // use "001" as fallback
            order = calPref.get("001");
        }

        String[] caltypes = order.getStringArray();
        if (commonlyUsed) {
            // we have all commonly used calendar for the target region
            return caltypes;
        }

        // if not commonlyUsed, add all preferred calendars in the order
        for (int i = 0; i < caltypes.length; i++) {
            values.add(caltypes[i]);
        }
        // then, add other available calendars
        for (CalType t : CalType.values()) {
            if (!values.contains(t.getId())) {
                values.add(t.getId());
            }
        }
        return values.toArray(new String[values.size()]);
    }

    /**
     * Returns this Calendar's current time.
     *
     * @return the current time.
     * @stable ICU 2.0
     */
    public final Date getTime() {
        return new Date(getTimeInMillis());
    }

    /**
     * Sets this Calendar's current time with the given Date.
     *
     * <p>Note: Calling <code>setTime</code> with <code>Date(Long.MAX_VALUE)</code> or <code>
     * Date(Long.MIN_VALUE)</code> may yield incorrect field values from {@link #get(int)}.
     *
     * @param date the given Date.
     * @stable ICU 2.0
     */
    public final void setTime(Date date) {
        setTimeInMillis(date.getTime());
    }

    /**
     * Returns this Calendar's current time as a long.
     *
     * @return the current time as UTC milliseconds from the epoch.
     * @stable ICU 2.0
     */
    public long getTimeInMillis() {
        if (!isTimeSet) updateTime();
        return time;
    }

    /**
     * Sets this Calendar's current time from the given long value. An IllegalIcuArgumentException
     * is thrown when millis is outside the range permitted by a Calendar object when in strict
     * mode. When in lenient mode the out of range values are pinned to their respective min/max.
     *
     * @param millis the new time in UTC milliseconds from the epoch.
     * @stable ICU 2.0
     */
    public void setTimeInMillis(long millis) {
        if (millis > MAX_MILLIS) {
            if (isLenient()) {
                millis = MAX_MILLIS;
            } else {
                throw new IllegalArgumentException(
                        "millis value greater than upper bounds for a Calendar : " + millis);
            }
        } else if (millis < MIN_MILLIS) {
            if (isLenient()) {
                millis = MIN_MILLIS;
            } else {
                throw new IllegalArgumentException(
                        "millis value less than lower bounds for a Calendar : " + millis);
            }
        }
        time = millis;
        areFieldsSet = areAllFieldsSet = false;
        isTimeSet = areFieldsVirtuallySet = true;

        Arrays.fill(fields, 0);
        Arrays.fill(stamp, (byte) 0);
        nextStamp = MINIMUM_USER_STAMP;
    }

    // -------------------------------------------------------------------------
    // Temporal Calendar API.
    // -------------------------------------------------------------------------
    /**
     * {@icu} Returns true if the date is in a leap year. Recalculate the current time field values
     * if the time value has been changed by a call to * setTime(). This method is semantically
     * const, but may alter the object in memory. A "leap year" is a year that contains more days
     * than other years (for solar or lunar calendars) or more months than other years (for
     * lunisolar calendars like Hebrew or Chinese), as defined in the ECMAScript Temporal proposal.
     *
     * @return true if the date in the fields is in a Temporal proposal defined leap year. False
     *     otherwise.
     * @stable ICU 74
     */
    public boolean inTemporalLeapYear() {
        // Default to Gregorian based leap year rule.
        return getActualMaximum(DAY_OF_YEAR) == 366;
    }

    private static String[] gTemporalMonthCodes = {
        "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12"
    };

    /**
     * Gets The Temporal monthCode value corresponding to the month for the date. The value is a
     * string identifier that starts with the literal grapheme "M" followed by two graphemes
     * representing the zero-padded month number of the current month in a normal (non-leap) year
     * and suffixed by an optional literal grapheme "L" if this is a leap month in a lunisolar
     * calendar. The 25 possible values are "M01" .. "M13" and "M01L" .. "M12L". For the Hebrew
     * calendar, the values are "M01" .. "M12" for non-leap year, and "M01" .. "M05", "M05L", "M06"
     * .. "M12" for leap year. For the Chinese calendar, the values are "M01" .. "M12" for non-leap
     * year and in leap year with another monthCode in "M01L" .. "M12L". For Coptic and Ethiopian
     * calendar, the Temporal monthCode values for any years are "M01" to "M13".
     *
     * @return One of 25 possible strings in {"M01".."M13", "M01L".."M12L"}.
     * @stable ICU 74
     */
    public String getTemporalMonthCode() {
        int month = get(MONTH);
        assert (month < 12);
        assert (internalGet(IS_LEAP_MONTH) == 0);
        return gTemporalMonthCodes[month];
    }

    /**
     * Sets The Temporal monthCode which is a string identifier that starts with the literal
     * grapheme "M" followed by two graphemes representing the zero-padded month number of the
     * current month in a normal (non-leap) year and suffixed by an optional literal grapheme "L" if
     * this is a leap month in a lunisolar calendar. The 25 possible values are "M01" .. "M13" and
     * "M01L" .. "M12L". For Hebrew calendar, the values are "M01" .. "M12" for non-leap years, and
     * "M01" .. "M05", "M05L", "M06" .. "M12" for leap year. For the Chinese calendar, the values
     * are "M01" .. "M12" for non-leap year and in leap year with another monthCode in "M01L" ..
     * "M12L". For Coptic and Ethiopian calendar, the Temporal monthCode values for any years are
     * "M01" to "M13".
     *
     * @param temporalMonth One of 25 possible strings in {"M01".. "M12", "M13", "M01L", "M12L"}.
     * @stable ICU 74
     */
    public void setTemporalMonthCode(String temporalMonth) {
        if (temporalMonth.length() == 3 && temporalMonth.charAt(0) == 'M') {
            for (int m = 0; m < gTemporalMonthCodes.length; m++) {
                if (temporalMonth.equals(gTemporalMonthCodes[m])) {
                    set(MONTH, m);
                    set(IS_LEAP_MONTH, 0);
                    return;
                }
            }
        }
        throw new IllegalArgumentException("Incorrect temporal Month code: " + temporalMonth);
    }

    // -------------------------------------------------------------------------
    // End of Temporal Calendar API
    // -------------------------------------------------------------------------

    /**
     * Returns the value for a given time field.
     *
     * @param field the given time field.
     * @return the value for the given time field.
     * @stable ICU 2.0
     */
    public final int get(int field) {
        complete();
        return fields[field];
    }

    /**
     * Returns the value for a given time field. This is an internal method for subclasses that does
     * <em>not</em> trigger any calculations.
     *
     * @param field the given time field.
     * @return the value for the given time field.
     * @stable ICU 2.0
     */
    protected final int internalGet(int field) {
        return fields[field];
    }

    /**
     * Returns the value for a given time field, or return the given default value if the field is
     * not set. This is an internal method for subclasses that does <em>not</em> trigger any
     * calculations.
     *
     * @param field the given time field.
     * @param defaultValue value to return if field is not set
     * @return the value for the given time field of defaultValue if the field is unset
     * @stable ICU 2.0
     */
    protected final int internalGet(int field, int defaultValue) {
        return (stamp[field] > UNSET) ? fields[field] : defaultValue;
    }

    /**
     * Use this function instead of internalGet(MONTH). The implementation check the timestamp of
     * MONTH and ORDINAL_MONTH and use the one set later. The subclass should override it to conver
     * the value of ORDINAL_MONTH to MONTH correctly if ORDINAL_MONTH has higher priority.
     *
     * @return the value for the given time field.
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    protected int internalGetMonth() {
        if (resolveFields(MONTH_PRECEDENCE) == MONTH) {
            return internalGet(MONTH);
        }
        return internalGet(ORDINAL_MONTH);
    }

    /**
     * Use this function instead of internalGet(MONTH, defaultValue). The implementation check the
     * timestamp of MONTH and ORDINAL_MONTH and use the one set later. The subclass should override
     * it to conver the value of ORDINAL_MONTH to MONTH correctly if ORDINAL_MONTH has higher
     * priority.
     *
     * @param defaultValue a default value used if the MONTH and ORDINAL_MONTH are both unset.
     * @return the value for the MONTH.
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    protected int internalGetMonth(int defaultValue) {
        if (resolveFields(MONTH_PRECEDENCE) == MONTH) {
            return internalGet(MONTH, defaultValue);
        }
        return internalGet(ORDINAL_MONTH, defaultValue);
    }

    /**
     * Sets the time field with the given value.
     *
     * @param field the given time field.
     * @param value the value to be set for the given time field.
     * @stable ICU 2.0
     */
    public final void set(int field, int value) {
        if (areFieldsVirtuallySet) {
            computeFields();
        }
        fields[field] = value;
        /* Ensure that the fNextStamp value doesn't go pass max value for 32 bit integer */
        if (nextStamp == STAMP_MAX) {
            recalculateStamp();
        }
        stamp[field] = nextStamp++;
        isTimeSet = areFieldsSet = areFieldsVirtuallySet = false;
    }

    /**
     * Sets the values for the fields year, month, and date. Previous values of other fields are
     * retained. If this is not desired, call {@link #clear()} first.
     *
     * @param year the value used to set the YEAR time field.
     * @param month the value used to set the MONTH time field. Month value is 0-based. e.g., 0 for
     *     January.
     * @param date the value used to set the DATE time field.
     * @stable ICU 2.0
     */
    public final void set(int year, int month, int date) {
        set(YEAR, year);
        set(MONTH, month);
        set(DATE, date);
    }

    /**
     * Sets the values for the fields year, month, date, hour, and minute. Previous values of other
     * fields are retained. If this is not desired, call {@link #clear()} first.
     *
     * @param year the value used to set the YEAR time field.
     * @param month the value used to set the MONTH time field. Month value is 0-based. e.g., 0 for
     *     January.
     * @param date the value used to set the DATE time field.
     * @param hour the value used to set the HOUR_OF_DAY time field.
     * @param minute the value used to set the MINUTE time field.
     * @stable ICU 2.0
     */
    public final void set(int year, int month, int date, int hour, int minute) {
        set(YEAR, year);
        set(MONTH, month);
        set(DATE, date);
        set(HOUR_OF_DAY, hour);
        set(MINUTE, minute);
    }

    /**
     * Sets the values for the fields year, month, date, hour, minute, and second. Previous values
     * of other fields are retained. If this is not desired, call {@link #clear} first.
     *
     * @param year the value used to set the YEAR time field.
     * @param month the value used to set the MONTH time field. Month value is 0-based. e.g., 0 for
     *     January.
     * @param date the value used to set the DATE time field.
     * @param hour the value used to set the HOUR_OF_DAY time field.
     * @param minute the value used to set the MINUTE time field.
     * @param second the value used to set the SECOND time field.
     * @stable ICU 2.0
     */
    public final void set(int year, int month, int date, int hour, int minute, int second) {
        set(YEAR, year);
        set(MONTH, month);
        set(DATE, date);
        set(HOUR_OF_DAY, hour);
        set(MINUTE, minute);
        set(SECOND, second);
    }

    /**
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    protected int getRelatedYearDifference() {
        return 0;
    }

    /**
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    public int getRelatedYear() {
        return get(EXTENDED_YEAR) + getRelatedYearDifference();
    }

    /**
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    public void setRelatedYear(int year) {
        set(EXTENDED_YEAR, year - getRelatedYearDifference());
    }

    /**
     * Clears the values of all the time fields.
     *
     * @stable ICU 2.0
     */
    public final void clear() {
        Arrays.fill(fields, 0);
        Arrays.fill(stamp, (byte) 0);
        nextStamp = MINIMUM_USER_STAMP;
        isTimeSet = areFieldsSet = areAllFieldsSet = areFieldsVirtuallySet = false;
    }

    /**
     * Clears the value in the given time field.
     *
     * @param field the time field to be cleared.
     * @stable ICU 2.0
     */
    public final void clear(int field) {
        if (areFieldsVirtuallySet) {
            computeFields();
        }
        fields[field] = 0;
        stamp[field] = UNSET;
        if (field == MONTH) {
            fields[ORDINAL_MONTH] = 0;
            stamp[ORDINAL_MONTH] = UNSET;
        }
        if (field == ORDINAL_MONTH) {
            fields[MONTH] = 0;
            stamp[MONTH] = UNSET;
        }
        isTimeSet = areFieldsSet = areAllFieldsSet = areFieldsVirtuallySet = false;
    }

    /**
     * Determines if the given time field has a value set.
     *
     * @return true if the given time field has a value set; false otherwise.
     * @stable ICU 2.0
     */
    public final boolean isSet(int field) {
        return areFieldsVirtuallySet || (stamp[field] != UNSET);
    }

    /**
     * Fills in any unset fields in the time field list.
     *
     * @stable ICU 2.0
     */
    protected void complete() {
        if (!isTimeSet) updateTime();
        if (!areFieldsSet) {
            computeFields(); // fills in unset fields
            areFieldsSet = true;
            areAllFieldsSet = true;
        }
    }

    /**
     * Compares this calendar to the specified object. The result is <code>true</code> if and only
     * if the argument is not <code>null</code> and is a <code>Calendar</code> object that
     * represents the same calendar as this object.
     *
     * @param obj the object to compare with.
     * @return <code>true</code> if the objects are the same; <code>false</code> otherwise.
     * @stable ICU 2.0
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (this == obj) {
            return true;
        }
        if (this.getClass() != obj.getClass()) {
            return false;
        }

        Calendar that = (Calendar) obj;

        return isEquivalentTo(that) && getTimeInMillis() == that.getTime().getTime();
    }

    /**
     * {@icu} Returns true if the given Calendar object is equivalent to this one. An equivalent
     * Calendar will behave exactly as this one does, but it may be set to a different time. By
     * contrast, for the equals() method to return true, the other Calendar must be set to the same
     * time.
     *
     * @param other the Calendar to be compared with this Calendar
     * @stable ICU 2.4
     */
    public boolean isEquivalentTo(Calendar other) {
        return this.getClass() == other.getClass()
                && isLenient() == other.isLenient()
                && getFirstDayOfWeek() == other.getFirstDayOfWeek()
                && getMinimalDaysInFirstWeek() == other.getMinimalDaysInFirstWeek()
                && getTimeZone().equals(other.getTimeZone())
                && getRepeatedWallTimeOption() == other.getRepeatedWallTimeOption()
                && getSkippedWallTimeOption() == other.getSkippedWallTimeOption();
    }

    /**
     * Returns a hash code for this calendar.
     *
     * @return a hash code value for this object.
     * @stable ICU 2.0
     */
    @Override
    public int hashCode() {
        /* Don't include the time because (a) we don't want the hash value to
         * move around just because a calendar is set to different times, and
         * (b) we don't want to trigger a time computation just to get a hash.
         * Note that it is not necessary for unequal objects to always have
         * unequal hashes, but equal objects must have equal hashes.  */
        return (lenient ? 1 : 0)
                | (firstDayOfWeek << 1)
                | (minimalDaysInFirstWeek << 4)
                | (repeatedWallTime << 7)
                | (skippedWallTime << 9)
                | (zone.hashCode() << 11);
    }

    /**
     * Returns the difference in milliseconds between the moment this calendar is set to and the
     * moment the given calendar or Date object is set to.
     */
    private long compare(Object that) {
        long thatMs;
        if (that instanceof Calendar) {
            thatMs = ((Calendar) that).getTimeInMillis();
        } else if (that instanceof Date) {
            thatMs = ((Date) that).getTime();
        } else {
            throw new IllegalArgumentException(that + "is not a Calendar or Date");
        }
        return getTimeInMillis() - thatMs;
    }

    /**
     * Compares the time field records. Equivalent to comparing result of conversion to UTC.
     *
     * @param when the Calendar to be compared with this Calendar.
     * @return true if the current time of this Calendar is before the time of Calendar when; false
     *     otherwise.
     * @stable ICU 2.0
     */
    public boolean before(Object when) {
        return compare(when) < 0;
    }

    /**
     * Compares the time field records. Equivalent to comparing result of conversion to UTC.
     *
     * @param when the Calendar to be compared with this Calendar.
     * @return true if the current time of this Calendar is after the time of Calendar when; false
     *     otherwise.
     * @stable ICU 2.0
     */
    public boolean after(Object when) {
        return compare(when) > 0;
    }

    /**
     * Returns the maximum value that this field could have, given the current date. For example,
     * with the Gregorian date February 3, 1997 and the {@link #DAY_OF_MONTH DAY_OF_MONTH} field,
     * the actual maximum is 28; for February 3, 1996 it is 29.
     *
     * <p>The actual maximum computation ignores smaller fields and the current value of like-sized
     * fields. For example, the actual maximum of the DAY_OF_YEAR or MONTH depends only on the year
     * and supra-year fields. The actual maximum of the DAY_OF_MONTH depends, in addition, on the
     * MONTH field and any other fields at that granularity (such as IS_LEAP_MONTH). The
     * DAY_OF_WEEK_IN_MONTH field does not depend on the current DAY_OF_WEEK; it returns the maximum
     * for any day of week in the current month. Likewise for the WEEK_OF_MONTH and WEEK_OF_YEAR
     * fields.
     *
     * @param field the field whose maximum is desired
     * @return the maximum of the given field for the current date of this calendar
     * @see #getMaximum
     * @see #getLeastMaximum
     * @stable ICU 2.0
     */
    public int getActualMaximum(int field) {
        int result;

        switch (field) {
            case DAY_OF_MONTH:
                {
                    Calendar cal = clone();
                    cal.setLenient(true);
                    cal.prepareGetActual(field, false);
                    result = handleGetMonthLength(cal.get(EXTENDED_YEAR), cal.get(MONTH));
                }
                break;

            case DAY_OF_YEAR:
                {
                    Calendar cal = clone();
                    cal.setLenient(true);
                    cal.prepareGetActual(field, false);
                    result = handleGetYearLength(cal.get(EXTENDED_YEAR));
                }
                break;

            case ERA:
            case DAY_OF_WEEK:
            case AM_PM:
            case HOUR:
            case HOUR_OF_DAY:
            case MINUTE:
            case SECOND:
            case MILLISECOND:
            case ZONE_OFFSET:
            case DST_OFFSET:
            case DOW_LOCAL:
            case JULIAN_DAY:
            case MILLISECONDS_IN_DAY:
                // These fields all have fixed minima/maxima
                result = getMaximum(field);
                break;

            case ORDINAL_MONTH:
                result =
                        inTemporalLeapYear()
                                ? getMaximum(ORDINAL_MONTH)
                                : getLeastMaximum(ORDINAL_MONTH);
                break;

            default:
                // For all other fields, do it the hard way....
                result = getActualHelper(field, getLeastMaximum(field), getMaximum(field));
                break;
        }
        return result;
    }

    /**
     * Returns the minimum value that this field could have, given the current date. For most
     * fields, this is the same as {@link #getMinimum getMinimum} and {@link #getGreatestMinimum
     * getGreatestMinimum}. However, some fields, especially those related to week number, are more
     * complicated.
     *
     * <p>For example, assume {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek} returns 4
     * and {@link #getFirstDayOfWeek getFirstDayOfWeek} returns SUNDAY. If the first day of the
     * month is Sunday, Monday, Tuesday, or Wednesday there will be four or more days in the first
     * week, so it will be week number 1, and <code>getActualMinimum(WEEK_OF_MONTH)</code> will
     * return 1. However, if the first of the month is a Thursday, Friday, or Saturday, there are
     * <em>not</em> four days in that week, so it is week number 0, and <code>
     * getActualMinimum(WEEK_OF_MONTH)</code> will return 0.
     *
     * <p>
     *
     * @param field the field whose actual minimum value is desired.
     * @return the minimum of the given field for the current date of this calendar
     * @see #getMinimum
     * @see #getGreatestMinimum
     * @stable ICU 2.0
     */
    public int getActualMinimum(int field) {
        int result;

        switch (field) {
            case DAY_OF_WEEK:
            case AM_PM:
            case HOUR:
            case HOUR_OF_DAY:
            case MINUTE:
            case SECOND:
            case MILLISECOND:
            case ZONE_OFFSET:
            case DST_OFFSET:
            case DOW_LOCAL:
            case JULIAN_DAY:
            case MILLISECONDS_IN_DAY:
                // These fields all have fixed minima/maxima
                result = getMinimum(field);
                break;

            default:
                // For all other fields, do it the hard way....
                result = getActualHelper(field, getGreatestMinimum(field), getMinimum(field));
                break;
        }
        return result;
    }

    /**
     * Prepare this calendar for computing the actual minimum or maximum. This method modifies this
     * calendar's fields; it is called on a temporary calendar.
     *
     * <p>Rationale: The semantics of getActualXxx() is to return the maximum or minimum value that
     * the given field can take, taking into account other relevant fields. In general these other
     * fields are larger fields. For example, when computing the actual maximum DAY_OF_MONTH, the
     * current value of DAY_OF_MONTH itself is ignored, as is the value of any field smaller.
     *
     * <p>The time fields all have fixed minima and maxima, so we don't need to worry about them.
     * This also lets us set the MILLISECONDS_IN_DAY to zero to erase any effects the time fields
     * might have when computing date fields.
     *
     * <p>DAY_OF_WEEK is adjusted specially for the WEEK_OF_MONTH and WEEK_OF_YEAR fields to ensure
     * that they are computed correctly.
     *
     * @stable ICU 2.0
     */
    protected void prepareGetActual(int field, boolean isMinimum) {
        set(MILLISECONDS_IN_DAY, 0);

        switch (field) {
            case YEAR:
            case EXTENDED_YEAR:
                set(DAY_OF_YEAR, getGreatestMinimum(DAY_OF_YEAR));
                break;

            case YEAR_WOY:
                set(WEEK_OF_YEAR, getGreatestMinimum(WEEK_OF_YEAR));
                break;

            case MONTH:
                set(DAY_OF_MONTH, getGreatestMinimum(DAY_OF_MONTH));
                break;

            case DAY_OF_WEEK_IN_MONTH:
                // For dowim, the maximum occurs for the DOW of the first of the
                // month.
                set(DAY_OF_MONTH, 1);
                set(DAY_OF_WEEK, get(DAY_OF_WEEK)); // Make this user set
                break;

            case WEEK_OF_MONTH:
            case WEEK_OF_YEAR:
                // If we're counting weeks, set the day of the week to either the
                // first or last localized DOW.  We know the last week of a month
                // or year will contain the first day of the week, and that the
                // first week will contain the last DOW.
                {
                    int dow = firstDayOfWeek;
                    if (isMinimum) {
                        dow = (dow + 6) % 7; // set to last DOW
                        if (dow < SUNDAY) {
                            dow += 7;
                        }
                    }
                    set(DAY_OF_WEEK, dow);
                }
                break;
        }

        // Do this last to give it the newest time stamp
        set(field, getGreatestMinimum(field));
    }

    private int getActualHelper(int field, int startValue, int endValue) {

        if (startValue == endValue) {
            // if we know that the maximum value is always the same, just return it
            return startValue;
        }

        final int delta = (endValue > startValue) ? 1 : -1;

        // clone the calendar so we don't mess with the real one, and set it to
        // accept anything for the field values
        Calendar work = clone();

        // need to resolve time here, otherwise, fields set for actual limit
        // may cause conflict with fields previously set (but not yet resolved).
        work.complete();

        work.setLenient(true);
        work.prepareGetActual(field, delta < 0);

        // now try each value from the start to the end one by one until
        // we get a value that normalizes to another value.  The last value that
        // normalizes to itself is the actual maximum for the current date

        work.set(field, startValue);
        // prepareGetActual sets the first day of week in the same week with
        // the first day of a month.  Unlike WEEK_OF_YEAR, week number for the
        // which week contains days from both previous and current month is
        // not unique.  For example, last several days in the previous month
        // is week 5, and the rest of week is week 1.
        if (work.get(field) != startValue && field != WEEK_OF_MONTH && delta > 0) {
            return startValue;
        }
        int result = startValue;
        do {
            startValue += delta;
            work.add(field, delta);
            if (work.get(field) != startValue) {
                break;
            }
            result = startValue;
        } while (startValue != endValue);

        return result;
    }

    /**
     * Rolls (up/down) a single unit of time on the given field. If the field is rolled past its
     * maximum allowable value, it will "wrap" back to its minimum and continue rolling. For
     * example, to roll the current date up by one day, you can call:
     *
     * <p><code>roll({@link #DATE}, true)</code>
     *
     * <p>When rolling on the {@link #YEAR} field, it will roll the year value in the range between
     * 1 and the value returned by calling {@link #getMaximum getMaximum}({@link #YEAR}).
     *
     * <p>When rolling on certain fields, the values of other fields may conflict and need to be
     * changed. For example, when rolling the <code>MONTH</code> field for the Gregorian date
     * 1/31/96 upward, the <code>DAY_OF_MONTH</code> field must be adjusted so that the result is
     * 2/29/96 rather than the invalid 2/31/96.
     *
     * <p>Rolling up always means rolling forward in time (unless the limit of the field is reached,
     * in which case it may pin or wrap), so for the Gregorian calendar, starting with 100 BC and
     * rolling the year up results in 99 BC. When eras have a definite beginning and end (as in the
     * Chinese calendar, or as in most eras in the Japanese calendar) then rolling the year past
     * either limit of the era will cause the year to wrap around. When eras only have a limit at
     * one end, then attempting to roll the year past that limit will result in pinning the year at
     * that limit. Note that for most calendars in which era 0 years move forward in time (such as
     * Buddhist, Hebrew, or Islamic), it is possible for add or roll to result in negative years for
     * era 0 (that is the only way to represent years before the calendar epoch in such calendars).
     *
     * <p><b>Note:</b> Calling {@code roll(field, true)} N times is <em>not</em> necessarily
     * equivalent to calling {@code roll(field, N)}. For example, imagine that you start with the
     * date Gregorian date January 31, 1995. If you call {@code roll(Calendar.MONTH, 2)}, the result
     * will be March 31, 1995. But if you call {@code roll(Calendar.MONTH, true)}, the result will
     * be February 28, 1995. Calling it one more time will give March 28, 1995, which is usually not
     * the desired result.
     *
     * <p><b>Note:</b> You should always use {@code roll} and {@code add} rather than attempting to
     * perform arithmetic operations directly on the fields of a {@code Calendar}. It is quite
     * possible for {@code Calendar} subclasses to have fields with non-linear behavior, for example
     * missing months or days during non-leap years. The subclasses' {@code add} and {@code roll}
     * methods will take this into account, while simple arithmetic manipulations may give invalid
     * results.
     *
     * <p>
     *
     * @param field the calendar field to roll.
     * @param up indicates if the value of the specified time field is to be rolled up or rolled
     *     down. Use <code>true</code> if rolling up, <code>false</code> otherwise.
     * @exception IllegalArgumentException if the field is invalid or refers to a field that cannot
     *     be handled by this method.
     * @see #roll(int, int)
     * @see #add
     * @stable ICU 2.0
     */
    public final void roll(int field, boolean up) {
        roll(field, up ? +1 : -1);
    }

    /**
     * Rolls (up/down) a specified amount time on the given field. For example, to roll the current
     * date up by three days, you can call <code>roll(Calendar.DATE, 3)</code>. If the field is
     * rolled past its maximum allowable value, it will "wrap" back to its minimum and continue
     * rolling. For example, calling <code>roll(Calendar.DATE, 10)</code> on a Gregorian calendar
     * set to 4/25/96 will result in the date 4/5/96.
     *
     * <p>When rolling on certain fields, the values of other fields may conflict and need to be
     * changed. For example, when rolling the {@link #MONTH MONTH} field for the Gregorian date
     * 1/31/96 by +1, the {@link #DAY_OF_MONTH DAY_OF_MONTH} field must be adjusted so that the
     * result is 2/29/96 rather than the invalid 2/31/96.
     *
     * <p>Rolling by a positive value always means rolling forward in time (unless the limit of the
     * field is reached, in which case it may pin or wrap), so for the Gregorian calendar, starting
     * with 100 BC and rolling the year by + 1 results in 99 BC. When eras have a definite beginning
     * and end (as in the Chinese calendar, or as in most eras in the Japanese calendar) then
     * rolling the year past either limit of the era will cause the year to wrap around. When eras
     * only have a limit at one end, then attempting to roll the year past that limit will result in
     * pinning the year at that limit. Note that for most calendars in which era 0 years move
     * forward in time (such as Buddhist, Hebrew, or Islamic), it is possible for add or roll to
     * result in negative years for era 0 (that is the only way to represent years before the
     * calendar epoch in such calendars).
     *
     * <p>{@icunote} the ICU implementation of this method is able to roll all fields except for
     * {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET}, and {@link #ZONE_OFFSET ZONE_OFFSET}.
     * Subclasses may, of course, add support for additional fields in their overrides of <code>roll
     * </code>.
     *
     * <p><b>Note:</b> You should always use {@code roll} and {@code add} rather than attempting to
     * perform arithmetic operations directly on the fields of a {@code Calendar}. It is quite
     * possible for {@code Calendar} subclasses to have fields with non-linear behavior, for example
     * missing months or days during non-leap years. The subclasses' {@code add} and {@code roll}
     * methods will take this into account, while simple arithmetic manipulations may give invalid
     * results.
     *
     * <p><b>Subclassing:</b><br>
     * This implementation of <code>roll</code> assumes that the behavior of the field is continuous
     * between its minimum and maximum, which are found by calling {@link #getActualMinimum
     * getActualMinimum} and {@link #getActualMaximum getActualMaximum}. For most such fields,
     * simple addition, subtraction, and modulus operations are sufficient to perform the roll. For
     * week-related fields, the results of {@link #getFirstDayOfWeek getFirstDayOfWeek} and {@link
     * #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek} are also necessary. Subclasses can
     * override these two methods if their values differ from the defaults.
     *
     * <p>Subclasses that have fields for which the assumption of continuity breaks down must
     * override <code>roll</code> to handle those fields specially. For example, in the Hebrew
     * calendar the month "Adar I" only occurs in leap years; in other years the calendar jumps from
     * Shevat (month #4) to Adar (month #6). The {@link HebrewCalendar#roll HebrewCalendar.roll}
     * method takes this into account, so that rolling the month of Shevat by one gives the proper
     * result (Adar) in a non-leap year.
     *
     * <p>
     *
     * @param field the calendar field to roll.
     * @param amount the amount by which the field should be rolled.
     * @exception IllegalArgumentException if the field is invalid or refers to a field that cannot
     *     be handled by this method.
     * @see #roll(int, boolean)
     * @see #add
     * @stable ICU 2.0
     */
    public void roll(int field, int amount) {

        if (amount == 0) {
            return; // Nothing to do
        }

        complete();

        switch (field) {
            case DAY_OF_MONTH:
            case AM_PM:
            case MINUTE:
            case SECOND:
            case MILLISECOND:
            case MILLISECONDS_IN_DAY:
            case ERA:
                // These are the standard roll instructions.  These work for all
                // simple cases, that is, cases in which the limits are fixed, such
                // as the hour, the day of the month, and the era.
                {
                    int min = getActualMinimum(field);
                    int max = getActualMaximum(field);
                    int gap = max - min + 1;

                    int value = internalGet(field) + amount;
                    value = (value - min) % gap;
                    if (value < 0) {
                        value += gap;
                    }
                    value += min;

                    set(field, value);
                    return;
                }

            case HOUR:
            case HOUR_OF_DAY:
                // Rolling the hour is difficult on the ONSET and CEASE days of
                // daylight savings.  For example, if the change occurs at
                // 2 AM, we have the following progression:
                // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst
                // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std
                // To get around this problem we don't use fields; we manipulate
                // the time in millis directly.
                {
                    // Assume min == 0 in calculations below
                    long start = getTimeInMillis();
                    int oldHour = internalGet(field);
                    int max = getMaximum(field);
                    int newHour = (oldHour + amount) % (max + 1);
                    if (newHour < 0) {
                        newHour += max + 1;
                    }
                    setTimeInMillis(start + ONE_HOUR * ((long) newHour - oldHour));
                    return;
                }

            case MONTH:
            case ORDINAL_MONTH:
                // Rolling the month involves both pinning the final value
                // and adjusting the DAY_OF_MONTH if necessary.  We only adjust the
                // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
                // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
                {
                    int max = getActualMaximum(MONTH);
                    int mon = (internalGetMonth() + amount) % (max + 1);

                    if (mon < 0) {
                        mon += (max + 1);
                    }
                    set(MONTH, mon);

                    // Keep the day of month in range.  We don't want to spill over
                    // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 ->
                    // mar3.
                    pinField(DAY_OF_MONTH);
                    return;
                }

            case YEAR:
            case YEAR_WOY:
                // * If era==0 and years go backwards in time, change sign of amount.
                // * Until we have new API per #9393, we temporarily hardcode knowledge of
                //   which calendars have era 0 years that go backwards.
                {
                    int era = get(ERA);
                    if (era == 0 && isEra0CountingBackward()) {
                        amount = -amount;
                    }
                    int newYear = internalGet(field) + amount;
                    if (era > 0 || newYear >= 1) {
                        int maxYear = getActualMaximum(field);
                        if (maxYear < 32768) {
                            // this era has real bounds, roll should wrap years
                            if (newYear < 1) {
                                newYear = maxYear - ((-newYear) % maxYear);
                            } else if (newYear > maxYear) {
                                newYear = ((newYear - 1) % maxYear) + 1;
                            }
                            // else era is unbounded, just pin low year instead of wrapping
                        } else if (newYear < 1) {
                            newYear = 1;
                        }
                        // else we are in era 0 with newYear < 1;
                        // calendars with years that go backwards must pin the year value at 0,
                        // other calendars can have years < 0 in era 0
                    } else if (era == 0 && isEra0CountingBackward()) {
                        newYear = 1;
                    }
                    set(field, newYear);
                    pinField(MONTH);
                    pinField(DAY_OF_MONTH);
                    return;
                }
            case EXTENDED_YEAR:
                // Rolling the year can involve pinning the DAY_OF_MONTH.
                set(field, internalGet(field) + amount);
                pinField(MONTH);
                pinField(DAY_OF_MONTH);
                return;

            case WEEK_OF_MONTH:
                {
                    // This is tricky, because during the roll we may have to shift
                    // to a different day of the week.  For example:

                    //    s  m  t  w  r  f  s
                    //          1  2  3  4  5
                    //    6  7  8  9 10 11 12

                    // When rolling from the 6th or 7th back one week, we go to the
                    // 1st (assuming that the first partial week counts).  The same
                    // thing happens at the end of the month.

                    // The other tricky thing is that we have to figure out whether
                    // the first partial week actually counts or not, based on the
                    // minimal first days in the week.  And we have to use the
                    // correct first day of the week to delineate the week
                    // boundaries.

                    // Here's our algorithm.  First, we find the real boundaries of
                    // the month.  Then we discard the first partial week if it
                    // doesn't count in this locale.  Then we fill in the ends with
                    // phantom days, so that the first partial week and the last
                    // partial week are full weeks.  We then have a nice square
                    // block of weeks.  We do the usual rolling within this block,
                    // as is done elsewhere in this method.  If we wind up on one of
                    // the phantom days that we added, we recognize this and pin to
                    // the first or the last day of the month.  Easy, eh?

                    // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
                    // in this locale.  We have dow in 0..6.
                    int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek();
                    if (dow < 0) dow += 7;

                    // Find the day of the week (normalized for locale) for the first
                    // of the month.
                    int fdm = (dow - internalGet(DAY_OF_MONTH) + 1) % 7;
                    if (fdm < 0) fdm += 7;

                    // Get the first day of the first full week of the month,
                    // including phantom days, if any.  Figure out if the first week
                    // counts or not; if it counts, then fill in phantom days.  If
                    // not, advance to the first real full week (skip the partial week).
                    int start;
                    if ((7 - fdm) < getMinimalDaysInFirstWeek())
                        start = 8 - fdm; // Skip the first partial week
                    else start = 1 - fdm; // This may be zero or negative

                    // Get the day of the week (normalized for locale) for the last
                    // day of the month.
                    int monthLen = getActualMaximum(DAY_OF_MONTH);
                    int ldm = (monthLen - internalGet(DAY_OF_MONTH) + dow) % 7;
                    // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here.

                    // Get the limit day for the blocked-off rectangular month; that
                    // is, the day which is one past the last day of the month,
                    // after the month has already been filled in with phantom days
                    // to fill out the last week.  This day has a normalized DOW of 0.
                    int limit = monthLen + 7 - ldm;

                    // Now roll between start and (limit - 1).
                    int gap = limit - start;
                    int day_of_month = (internalGet(DAY_OF_MONTH) + amount * 7 - start) % gap;
                    if (day_of_month < 0) day_of_month += gap;
                    day_of_month += start;

                    // Finally, pin to the real start and end of the month.
                    if (day_of_month < 1) day_of_month = 1;
                    if (day_of_month > monthLen) day_of_month = monthLen;

                    // Set the DAY_OF_MONTH.  We rely on the fact that this field
                    // takes precedence over everything else (since all other fields
                    // are also set at this point).  If this fact changes (if the
                    // disambiguation algorithm changes) then we will have to unset
                    // the appropriate fields here so that DAY_OF_MONTH is attended
                    // to.
                    set(DAY_OF_MONTH, day_of_month);
                    return;
                }
            case WEEK_OF_YEAR:
                {
                    // This follows the outline of WEEK_OF_MONTH, except it applies
                    // to the whole year.  Please see the comment for WEEK_OF_MONTH
                    // for general notes.

                    // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
                    // in this locale.  We have dow in 0..6.
                    int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek();
                    if (dow < 0) dow += 7;

                    // Find the day of the week (normalized for locale) for the first
                    // of the year.
                    int fdy = (dow - internalGet(DAY_OF_YEAR) + 1) % 7;
                    if (fdy < 0) fdy += 7;

                    // Get the first day of the first full week of the year,
                    // including phantom days, if any.  Figure out if the first week
                    // counts or not; if it counts, then fill in phantom days.  If
                    // not, advance to the first real full week (skip the partial week).
                    int start;
                    if ((7 - fdy) < getMinimalDaysInFirstWeek())
                        start = 8 - fdy; // Skip the first partial week
                    else start = 1 - fdy; // This may be zero or negative

                    // Get the day of the week (normalized for locale) for the last
                    // day of the year.
                    int yearLen = getActualMaximum(DAY_OF_YEAR);
                    int ldy = (yearLen - internalGet(DAY_OF_YEAR) + dow) % 7;
                    // We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here.

                    // Get the limit day for the blocked-off rectangular year; that
                    // is, the day which is one past the last day of the year,
                    // after the year has already been filled in with phantom days
                    // to fill out the last week.  This day has a normalized DOW of 0.
                    int limit = yearLen + 7 - ldy;

                    // Now roll between start and (limit - 1).
                    int gap = limit - start;
                    int day_of_year = (internalGet(DAY_OF_YEAR) + amount * 7 - start) % gap;
                    if (day_of_year < 0) day_of_year += gap;
                    day_of_year += start;

                    // Finally, pin to the real start and end of the month.
                    if (day_of_year < 1) day_of_year = 1;
                    if (day_of_year > yearLen) day_of_year = yearLen;

                    // Make sure that the year and day of year are attended to by
                    // clearing other fields which would normally take precedence.
                    // If the disambiguation algorithm is changed, this section will
                    // have to be updated as well.
                    set(DAY_OF_YEAR, day_of_year);
                    clear(MONTH);
                    clear(ORDINAL_MONTH);
                    return;
                }
            case DAY_OF_YEAR:
                {
                    // Roll the day of year using millis.  Compute the millis for
                    // the start of the year, and get the length of the year.
                    long delta = amount * ONE_DAY; // Scale up from days to millis
                    long min2 = time - (internalGet(DAY_OF_YEAR) - 1) * ONE_DAY;
                    int yearLength = getActualMaximum(DAY_OF_YEAR);
                    time = (time + delta - min2) % (yearLength * ONE_DAY);
                    if (time < 0) time += yearLength * ONE_DAY;
                    setTimeInMillis(time + min2);
                    return;
                }
            case DAY_OF_WEEK:
            case DOW_LOCAL:
                {
                    // Roll the day of week using millis.  Compute the millis for
                    // the start of the week, using the first day of week setting.
                    // Restrict the millis to [start, start+7days).
                    long delta = amount * ONE_DAY; // Scale up from days to millis
                    // Compute the number of days before the current day in this
                    // week.  This will be a value 0..6.
                    int leadDays = internalGet(field);
                    leadDays -= (field == DAY_OF_WEEK) ? getFirstDayOfWeek() : 1;
                    if (leadDays < 0) leadDays += 7;
                    long min2 = time - leadDays * ONE_DAY;
                    time = (time + delta - min2) % ONE_WEEK;
                    if (time < 0) time += ONE_WEEK;
                    setTimeInMillis(time + min2);
                    return;
                }
            case DAY_OF_WEEK_IN_MONTH:
                {
                    // Roll the day of week in the month using millis.  Determine
                    // the first day of the week in the month, and then the last,
                    // and then roll within that range.
                    long delta = amount * ONE_WEEK; // Scale up from weeks to millis
                    // Find the number of same days of the week before this one
                    // in this month.
                    int preWeeks = (internalGet(DAY_OF_MONTH) - 1) / 7;
                    // Find the number of same days of the week after this one
                    // in this month.
                    int postWeeks =
                            (getActualMaximum(DAY_OF_MONTH) - internalGet(DAY_OF_MONTH)) / 7;
                    // From these compute the min and gap millis for rolling.
                    long min2 = time - preWeeks * ONE_WEEK;
                    long gap2 = ONE_WEEK * (preWeeks + postWeeks + 1); // Must add 1!
                    // Roll within this range
                    time = (time + delta - min2) % gap2;
                    if (time < 0) time += gap2;
                    setTimeInMillis(time + min2);
                    return;
                }
            case JULIAN_DAY:
                set(field, internalGet(field) + amount);
                return;
            default:
                // Other fields cannot be rolled by this method
                throw new IllegalArgumentException(
                        "Calendar.roll(" + fieldName(field) + ") not supported");
        }
    }

    /**
     * Add a signed amount to a specified field, using this calendar's rules. For example, to add
     * three days to the current date, you can call <code>add(Calendar.DATE, 3)</code>.
     *
     * <p>When adding to certain fields, the values of other fields may conflict and need to be
     * changed. For example, when adding one to the {@link #MONTH MONTH} field for the Gregorian
     * date 1/31/96, the {@link #DAY_OF_MONTH DAY_OF_MONTH} field must be adjusted so that the
     * result is 2/29/96 rather than the invalid 2/31/96.
     *
     * <p>Adding a positive value always means moving forward in time, so for the Gregorian
     * calendar, starting with 100 BC and adding +1 to year results in 99 BC (even though this
     * actually reduces the numeric value of the field itself).
     *
     * <p>{@icunote} The ICU implementation of this method is able to add to all fields except for
     * {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET}, and {@link #ZONE_OFFSET ZONE_OFFSET}.
     * Subclasses may, of course, add support for additional fields in their overrides of <code>add
     * </code>.
     *
     * <p><b>Note:</b> You should always use {@code roll} and {@code add} rather than attempting to
     * perform arithmetic operations directly on the fields of a {@code Calendar}. It is quite
     * possible for {@code Calendar} subclasses to have fields with non-linear behavior, for example
     * missing months or days during non-leap years. The subclasses' {@code add} and {@code roll}
     * methods will take this into account, while simple arithmetic manipulations may give invalid
     * results.
     *
     * <p><b>Subclassing:</b><br>
     * This implementation of <code>add</code> assumes that the behavior of the field is continuous
     * between its minimum and maximum, which are found by calling {@link #getActualMinimum
     * getActualMinimum} and {@link #getActualMaximum getActualMaximum}. For such fields, simple
     * arithmetic operations are sufficient to perform the add.
     *
     * <p>Subclasses that have fields for which this assumption of continuity breaks down must
     * override <code>add</code> to handle those fields specially. For example, in the Hebrew
     * calendar the month "Adar I" only occurs in leap years; in other years the calendar jumps from
     * Shevat (month #4) to Adar (month #6). The {@link HebrewCalendar#add HebrewCalendar.add}
     * method takes this into account, so that adding one month to a date in Shevat gives the proper
     * result (Adar) in a non-leap year.
     *
     * <p>
     *
     * @param field the time field.
     * @param amount the amount to add to the field.
     * @exception IllegalArgumentException if the field is invalid or refers to a field that cannot
     *     be handled by this method.
     * @see #roll(int, int)
     * @stable ICU 2.0
     */
    @SuppressWarnings("fallthrough")
    public void add(int field, int amount) {

        if (amount == 0) {
            return; // Do nothing!
        }

        // We handle most fields in the same way.  The algorithm is to add
        // a computed amount of millis to the current millis.  The only
        // wrinkle is with DST (and/or a change to the zone's UTC offset, which
        // we'll include with DST) -- for some fields, like the DAY_OF_MONTH,
        // we don't want the wall time to shift due to changes in DST.  If the
        // result of the add operation is to move from DST to Standard, or
        // vice versa, we need to adjust by an hour forward or back,
        // respectively.  For such fields we set keepWallTimeInvariant to true.

        // We only adjust the DST for fields larger than an hour.  For
        // fields smaller than an hour, we cannot adjust for DST without
        // causing problems.  for instance, if you add one hour to April 5,
        // 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an
        // illegal value), but then the adjustment sees the change and
        // compensates by subtracting an hour.  As a result the time
        // doesn't advance at all.

        // For some fields larger than a day, such as a MONTH, we pin the
        // DAY_OF_MONTH.  This allows <March 31>.add(MONTH, 1) to be
        // <April 30>, rather than <April 31> => <May 1>.

        long delta = amount; // delta in ms
        boolean keepWallTimeInvariant = true;

        switch (field) {
            case ERA:
                set(field, get(field) + amount);
                pinField(ERA);
                return;

            case YEAR:
            case YEAR_WOY:
                // * If era=0 and years go backwards in time, change sign of amount.
                // * Until we have new API per #9393, we temporarily hardcode knowledge of
                //   which calendars have era 0 years that go backwards.
                // * Note that for YEAR (but not YEAR_WOY) we could instead handle
                //   this by applying the amount to the EXTENDED_YEAR field; but since
                //   we would still need to handle YEAR_WOY as below, might as well
                //   also handle YEAR the same way.
                {
                    int era = get(ERA);
                    if (era == 0 && isEra0CountingBackward()) {
                        amount = -amount;
                    }
                }
            // Fall through into standard handling
            case EXTENDED_YEAR:
            case MONTH:
            case ORDINAL_MONTH:
                {
                    boolean oldLenient = isLenient();
                    setLenient(true);
                    set(field, get(field) + amount);
                    pinField(DAY_OF_MONTH);
                    if (oldLenient == false) {
                        complete();
                        setLenient(oldLenient);
                    }
                }
                return;

            case WEEK_OF_YEAR:
            case WEEK_OF_MONTH:
            case DAY_OF_WEEK_IN_MONTH:
                delta *= ONE_WEEK;
                break;

            case AM_PM:
                delta *= 12 * ONE_HOUR;
                break;

            case DAY_OF_MONTH:
            case DAY_OF_YEAR:
            case DAY_OF_WEEK:
            case DOW_LOCAL:
            case JULIAN_DAY:
                delta *= ONE_DAY;
                break;

            case HOUR_OF_DAY:
            case HOUR:
                delta *= ONE_HOUR;
                keepWallTimeInvariant = false;
                break;

            case MINUTE:
                delta *= ONE_MINUTE;
                keepWallTimeInvariant = false;
                break;

            case SECOND:
                delta *= ONE_SECOND;
                keepWallTimeInvariant = false;
                break;

            case MILLISECOND:
            case MILLISECONDS_IN_DAY:
                keepWallTimeInvariant = false;
                break;

            default:
                throw new IllegalArgumentException(
                        "Calendar.add(" + fieldName(field) + ") not supported");
        }

        // In order to keep the wall time invariant (for fields where this is
        // appropriate), check the combined DST & ZONE offset before and
        // after the add() operation. If it changes, then adjust the millis
        // to compensate.
        int prevOffset = 0;
        int prevWallTime = 0;
        if (keepWallTimeInvariant) {
            prevOffset = get(DST_OFFSET) + get(ZONE_OFFSET);
            prevWallTime = get(MILLISECONDS_IN_DAY);
        }

        setTimeInMillis(getTimeInMillis() + delta);

        if (keepWallTimeInvariant) {
            int newWallTime = get(MILLISECONDS_IN_DAY);
            if (newWallTime != prevWallTime) {
                // There is at least one zone transition between the base
                // time and the result time. As the result, wall time has
                // changed.
                long t = internalGetTimeInMillis();
                int newOffset = get(DST_OFFSET) + get(ZONE_OFFSET);
                if (newOffset != prevOffset) {
                    // When the difference of the previous UTC offset and
                    // the new UTC offset exceeds 1 full day, we do not want
                    // to roll over/back the date. For now, this only happens
                    // in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452.
                    long adjAmount = (prevOffset - newOffset) % ONE_DAY;
                    if (adjAmount != 0) {
                        setTimeInMillis(t + adjAmount);
                        newWallTime = get(MILLISECONDS_IN_DAY);
                    }
                    if (newWallTime != prevWallTime) {
                        // The result wall time or adjusted wall time was shifted because
                        // the target wall time does not exist on the result date.
                        switch (skippedWallTime) {
                            case WALLTIME_FIRST:
                                if (adjAmount > 0) {
                                    setTimeInMillis(t);
                                }
                                break;
                            case WALLTIME_LAST:
                                if (adjAmount < 0) {
                                    setTimeInMillis(t);
                                }
                                break;
                            case WALLTIME_NEXT_VALID:
                                long tmpT = adjAmount > 0 ? internalGetTimeInMillis() : t;
                                Long immediatePrevTrans = getImmediatePreviousZoneTransition(tmpT);
                                if (immediatePrevTrans != null) {
                                    setTimeInMillis(immediatePrevTrans);
                                } else {
                                    throw new RuntimeException(
                                            "Could not locate a time zone transition before "
                                                    + tmpT);
                                }
                                break;
                        }
                    }
                }
            }
        }
    }

    /**
     * Returns the name of this calendar in the language of the given locale.
     *
     * @stable ICU 2.0
     */
    public String getDisplayName(Locale loc) {
        return this.getClass().getName();
    }

    /**
     * Returns the name of this calendar in the language of the given locale.
     *
     * @stable ICU 3.2
     */
    public String getDisplayName(ULocale loc) {
        return this.getClass().getName();
    }

    /**
     * Compares the times (in millis) represented by two <code>Calendar</code> objects.
     *
     * @param that the <code>Calendar</code> to compare to this.
     * @return <code>0</code> if the time represented by this <code>Calendar</code> is equal to the
     *     time represented by that <code>Calendar</code>, a value less than <code>0</code> if the
     *     time represented by this is before the time represented by that, and a value greater than
     *     <code>0</code> if the time represented by this is after the time represented by that.
     * @throws NullPointerException if that <code>Calendar</code> is null.
     * @throws IllegalArgumentException if the time of that <code>Calendar</code> can't be obtained
     *     because of invalid calendar values.
     * @stable ICU 3.4
     */
    @Override
    public int compareTo(Calendar that) {
        long v = getTimeInMillis() - that.getTimeInMillis();
        return v < 0 ? -1 : (v > 0 ? 1 : 0);
    }

    // -------------------------------------------------------------------------
    // Interface for creating custom DateFormats for different types of Calendars
    // -------------------------------------------------------------------------

    /**
     * {@icu} Returns a <code>DateFormat</code> appropriate to this calendar. Subclasses wishing to
     * specialize this behavior should override {@link #handleGetDateFormat}.
     *
     * @stable ICU 2.0
     */
    public DateFormat getDateTimeFormat(int dateStyle, int timeStyle, Locale loc) {
        return formatHelper(this, ULocale.forLocale(loc), dateStyle, timeStyle);
    }

    /**
     * {@icu} Returns a <code>DateFormat</code> appropriate to this calendar. Subclasses wishing to
     * specialize this behavior should override {@link #handleGetDateFormat}.
     *
     * @stable ICU 3.2
     */
    public DateFormat getDateTimeFormat(int dateStyle, int timeStyle, ULocale loc) {
        return formatHelper(this, loc, dateStyle, timeStyle);
    }

    /**
     * Creates a <code>DateFormat</code> appropriate to this calendar. This is a framework method
     * for subclasses to override. This method is responsible for creating the calendar-specific
     * DateFormat and DateFormatSymbols objects as needed.
     *
     * @param pattern the pattern, specific to the <code>DateFormat</code> subclass
     * @param locale the locale for which the symbols should be drawn
     * @return a <code>DateFormat</code> appropriate to this calendar
     * @stable ICU 2.0
     */
    protected DateFormat handleGetDateFormat(String pattern, Locale locale) {
        return handleGetDateFormat(pattern, null, ULocale.forLocale(locale));
    }

    /**
     * Creates a <code>DateFormat</code> appropriate to this calendar. This is a framework method
     * for subclasses to override. This method is responsible for creating the calendar-specific
     * DateFormat and DateFormatSymbols objects as needed.
     *
     * @param pattern the pattern, specific to the <code>DateFormat</code> subclass
     * @param override The override string. A numbering system override string can take one of the
     *     following forms: 1). If just a numbering system name is specified, it applies to all
     *     numeric fields in the date format pattern. 2). To specify an alternate numbering system
     *     on a field by field basis, use the field letters from the pattern followed by an = sign,
     *     followed by the numbering system name. For example, to specify that just the year be
     *     formatted using Hebrew digits, use the override "y=hebr". Multiple overrides can be
     *     specified in a single string by separating them with a semi-colon. For example, the
     *     override string "m=thai;y=deva" would format using Thai digits for the month and
     *     Devanagari digits for the year.
     * @param locale the locale for which the symbols should be drawn
     * @return a <code>DateFormat</code> appropriate to this calendar
     * @stable ICU 4.2
     */
    protected DateFormat handleGetDateFormat(String pattern, String override, Locale locale) {
        return handleGetDateFormat(pattern, override, ULocale.forLocale(locale));
    }

    /**
     * Creates a <code>DateFormat</code> appropriate to this calendar. This is a framework method
     * for subclasses to override. This method is responsible for creating the calendar-specific
     * DateFormat and DateFormatSymbols objects as needed.
     *
     * @param pattern the pattern, specific to the <code>DateFormat</code> subclass
     * @param locale the locale for which the symbols should be drawn
     * @return a <code>DateFormat</code> appropriate to this calendar
     * @stable ICU 2.0
     */
    protected DateFormat handleGetDateFormat(String pattern, ULocale locale) {
        return handleGetDateFormat(pattern, null, locale);
    }

    /**
     * Creates a <code>DateFormat</code> appropriate to this calendar. This is a framework method
     * for subclasses to override. This method is responsible for creating the calendar-specific
     * DateFormat and DateFormatSymbols objects as needed.
     *
     * @param pattern the pattern, specific to the <code>DateFormat</code> subclass
     * @param locale the locale for which the symbols should be drawn
     * @return a <code>DateFormat</code> appropriate to this calendar
     * @draft ICU 3.2 (retain)
     */
    protected DateFormat handleGetDateFormat(String pattern, String override, ULocale locale) {
        FormatConfiguration fmtConfig = new FormatConfiguration();
        fmtConfig.pattern = pattern;
        fmtConfig.override = override;
        fmtConfig.formatData = new DateFormatSymbols(this, locale);
        fmtConfig.loc = locale;
        fmtConfig.cal = this;

        return SimpleDateFormat.getInstance(fmtConfig);
    }

    // date format pattern cache
    private static final ICUCache<String, PatternData> PATTERN_CACHE = new SimpleCache<>();
    // final fallback patterns (match current root)
    private static final String[] DEFAULT_PATTERNS = {
        "HH:mm:ss z",
        "HH:mm:ss z",
        "HH:mm:ss",
        "HH:mm",
        "y MMMM d, EEEE",
        "y MMMM d",
        "y MMM d",
        "y-MM-dd",
        "{1} {0}",
        "{1} {0}",
        "{1} {0}",
        "{1} {0}",
        "{1} {0}"
    };
    private static final String[] DEFAULT_ATTIME_PATTERNS = {
        "{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"
    };
    // final fallback patterns
    private static final String[] TIME_SKELETONS = {
        "jmmsszzzz", // Full
        "jmmssz", // Long
        "jmmss", // Medium
        "jmm" // Short
    };

    private static DateFormat formatHelper(
            Calendar cal, ULocale loc, int dateStyle, int timeStyle) {
        if (timeStyle < DateFormat.NONE || timeStyle > DateFormat.SHORT) {
            throw new IllegalArgumentException("Illegal time style " + timeStyle);
        }
        if (dateStyle < DateFormat.NONE || dateStyle > DateFormat.SHORT) {
            throw new IllegalArgumentException("Illegal date style " + dateStyle);
        }

        PatternData patternData = PatternData.make(cal, loc);
        String override = null;

        // Resolve a pattern for the date/time style
        String pattern = null;
        if ((timeStyle >= 0) && (dateStyle >= 0)) {
            pattern =
                    SimpleFormatterImpl.formatRawPattern(
                            patternData.getDateAtTimePattern(dateStyle),
                            2,
                            2,
                            patternData.patterns[timeStyle],
                            patternData.patterns[dateStyle + 4]);
            // Might need to merge the overrides from the date and time into a single
            // override string TODO: Right now we are forcing the date's override into the
            // time style.
            if (patternData.overrides != null) {
                String dateOverride = patternData.overrides[dateStyle + 4];
                String timeOverride = patternData.overrides[timeStyle];
                override =
                        mergeOverrideStrings(
                                patternData.patterns[dateStyle + 4],
                                patternData.patterns[timeStyle],
                                dateOverride,
                                timeOverride);
            }
        } else if (timeStyle >= 0) {
            pattern = patternData.patterns[timeStyle];
            if (patternData.overrides != null) {
                override = patternData.overrides[timeStyle];
            }
        } else if (dateStyle >= 0) {
            pattern = patternData.patterns[dateStyle + 4];
            if (patternData.overrides != null) {
                override = patternData.overrides[dateStyle + 4];
            }
        } else {
            throw new IllegalArgumentException("No date or time style specified");
        }
        DateFormat result = cal.handleGetDateFormat(pattern, override, loc);
        result.setCalendar(cal);
        return result;
    }

    static class PatternData {
        // TODO make this even more object oriented
        private String[] patterns;
        private String[] overrides;
        private String[] atTimePatterns;

        public PatternData(String[] patterns, String[] overrides, String[] atTimePatterns) {
            this.patterns = patterns;
            this.overrides = overrides;
            this.atTimePatterns = atTimePatterns;
        }

        private String getDateTimePattern(int dateStyle) {
            int glueIndex = 8;
            if (patterns.length >= 13) {
                glueIndex += (dateStyle + 1);
            }
            final String dateTimePattern = patterns[glueIndex];
            return dateTimePattern;
        }

        private String getDateAtTimePattern(int dateStyle) {
            if (atTimePatterns != null && atTimePatterns.length >= 4) {
                final String dateTimePattern = atTimePatterns[dateStyle];
                return dateTimePattern;
            } else {
                return getDateTimePattern(dateStyle);
            }
        }

        private static PatternData make(Calendar cal, ULocale loc) {
            // First, try to get a pattern from PATTERN_CACHE
            String calType = cal.getType();
            String key = loc.getBaseName() + "+" + calType;
            PatternData patternData = null;
            boolean hasHourCycleKeywords =
                    loc.getKeywordValue("rg") != null || loc.getKeywordValue("hours") != null;
            if (!hasHourCycleKeywords) {
                // don't look in the cache if the locale specifies the rg or hc ("hours") keywords
                patternData = PATTERN_CACHE.get(key);
            }
            if (patternData == null) {
                // Cache missed.  Get one from bundle
                try {
                    patternData = getPatternData(loc, calType);
                } catch (MissingResourceException e) {
                    patternData = new PatternData(DEFAULT_PATTERNS, null, DEFAULT_ATTIME_PATTERNS);
                }
                if (!hasHourCycleKeywords) {
                    PATTERN_CACHE.put(key, patternData);
                }
            }
            return patternData;
        }
    }

    /**
     * Retrieves the DateTime patterns and overrides from the resource bundle and generates a new
     * PatternData object.
     *
     * @param locale Locale to retrieve.
     * @param calType Calendar type to retrieve. If not found will fallback to gregorian.
     * @return PatternData object for this locale and calendarType.
     */
    private static PatternData getPatternData(ULocale locale, String calType) {
        ICUResourceBundle rb =
                (ICUResourceBundle)
                        UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale);
        ICUResourceBundle dtPatternsRb =
                rb.findWithFallback("calendar/" + calType + "/DateTimePatterns");
        if (dtPatternsRb == null) {
            dtPatternsRb = rb.getWithFallback("calendar/gregorian/DateTimePatterns");
        }

        int patternsSize = dtPatternsRb.getSize();
        String[] dateTimePatterns = new String[patternsSize];
        String[] dateTimePatternsOverrides = new String[patternsSize];
        int i = 0; // index for dateTimePatterns, dateTimePatternsOverrides

        boolean useDTPG = false;
        if (locale.getKeywordValue("rg") != null || locale.getKeywordValue("hours") != null) {
            useDTPG = true;
        } else {
            String baseLocID = locale.getBaseName();
            if (!baseLocID.isEmpty() && !baseLocID.equals("und")) {
                ULocale baseLoc = new ULocale(baseLocID);
                // The following is different from ICU4C, where we can get the valid locale
                // for the SimpleDateFormat object. Here we do not have a SimpleDateFormat and
                // valid locale for the Calendar is a bit meaningless.
                ULocale validLoc = ULocale.addLikelySubtags(dtPatternsRb.getULocale());
                if (validLoc != baseLoc) {
                    String baseReg = baseLoc.getCountry();
                    if ((!baseReg.isEmpty() && !baseReg.equals(validLoc.getCountry()))
                            || !baseLoc.getLanguage().equals(validLoc.getLanguage())) {
                        useDTPG = true;
                    }
                }
            }
        }
        if (useDTPG) {
            // use DTPG if the standard time formats may have the wrong time cycle,
            // because the valid locale differs in important ways (region, language)
            // from the base locale.
            // We could *also* check whether they do actually have a mismatch with
            // the time cycle preferences for the region, but that is a lot more
            // work for little or no additional benefit, since just going ahead
            // and always synthesizing the time format as per the following should
            // create a locale-appropriate pattern with cycle that matches the
            // region preferences anyway.
            // In this case we get the first 4 entries of dateTimePatterns using
            // DateTimePatternGenerator, not resource data.
            DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstanceNoStdPat(locale);
            for (; i < TIME_SKELETONS.length; i++) {
                dateTimePatterns[i] = dtpg.getBestPattern(TIME_SKELETONS[i]);
            }
        }

        for (; i < patternsSize; i++) { // get all or remaining dateTimePatterns entries
            ICUResourceBundle concatenationPatternRb = (ICUResourceBundle) dtPatternsRb.get(i);
            switch (concatenationPatternRb.getType()) {
                case UResourceBundle.STRING:
                    dateTimePatterns[i] = concatenationPatternRb.getString();
                    break;
                case UResourceBundle.ARRAY:
                    dateTimePatterns[i] = concatenationPatternRb.getString(0);
                    dateTimePatternsOverrides[i] = concatenationPatternRb.getString(1);
                    break;
            }
        }

        dtPatternsRb = rb.findWithFallback("calendar/" + calType + "/DateTimePatterns%atTime");
        if (dtPatternsRb == null) {
            dtPatternsRb = rb.findWithFallback("calendar/gregorian/DateTimePatterns%atTime");
        }
        String[] atTimePatterns = null;
        if (dtPatternsRb != null) {
            patternsSize = dtPatternsRb.getSize();
            atTimePatterns = new String[patternsSize];
            if (patternsSize >= 4) {
                for (i = 0; i < 4; i++) {
                    ICUResourceBundle concatenationPatternRb =
                            (ICUResourceBundle) dtPatternsRb.get(i);
                    if (concatenationPatternRb.getType() == UResourceBundle.STRING) {
                        atTimePatterns[i] = concatenationPatternRb.getString();
                    }
                }
            }
        }

        return new PatternData(dateTimePatterns, dateTimePatternsOverrides, atTimePatterns);
    }

    /**
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    public static String getDateTimePattern(Calendar cal, ULocale uLocale, int dateStyle) {
        PatternData patternData = PatternData.make(cal, uLocale);
        return patternData.getDateTimePattern(dateStyle);
    }

    /**
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    public static String getDateAtTimePattern(Calendar cal, ULocale uLocale, int dateStyle) {
        PatternData patternData = PatternData.make(cal, uLocale);
        return patternData.getDateAtTimePattern(dateStyle);
    }

    private static String mergeOverrideStrings(
            String datePattern, String timePattern, String dateOverride, String timeOverride) {

        if (dateOverride == null && timeOverride == null) {
            return null;
        }

        if (dateOverride == null) {
            return expandOverride(timePattern, timeOverride);
        }

        if (timeOverride == null) {
            return expandOverride(datePattern, dateOverride);
        }

        if (dateOverride.equals(timeOverride)) {
            return dateOverride;
        }

        return (expandOverride(datePattern, dateOverride)
                + ";"
                + expandOverride(timePattern, timeOverride));
    }

    private static final char QUOTE = '\'';

    private static String expandOverride(String pattern, String override) {

        if (override.indexOf('=') >= 0) {
            return override;
        }
        boolean inQuotes = false;
        char prevChar = ' ';
        StringBuilder result = new StringBuilder();

        StringCharacterIterator it = new StringCharacterIterator(pattern);

        for (char c = it.first(); c != StringCharacterIterator.DONE; c = it.next()) {
            if (c == QUOTE) {
                inQuotes = !inQuotes;
                prevChar = c;
                continue;
            }
            if (!inQuotes && c != prevChar) {
                if (result.length() > 0) {
                    result.append(";");
                }
                result.append(c);
                result.append("=");
                result.append(override);
            }
            prevChar = c;
        }
        return result.toString();
    }

    /**
     * An instance of FormatConfiguration represents calendar specific date format configuration and
     * used for calling the ICU private SimpleDateFormat factory method.
     *
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    public static class FormatConfiguration {
        private String pattern;
        private String override;
        private DateFormatSymbols formatData;
        private Calendar cal;
        private ULocale loc;

        // Only Calendar can instantiate
        private FormatConfiguration() {}

        /**
         * Returns the pattern string
         *
         * @return the format pattern string
         * @internal
         * @deprecated This API is ICU internal only.
         */
        @Deprecated
        public String getPatternString() {
            return pattern;
        }

        /**
         * @internal
         * @deprecated This API is ICU internal only.
         */
        @Deprecated
        public String getOverrideString() {
            return override;
        }

        /**
         * Returns the calendar
         *
         * @return the calendar
         * @internal
         * @deprecated This API is ICU internal only.
         */
        @Deprecated
        public Calendar getCalendar() {
            return cal;
        }

        /**
         * Returns the locale
         *
         * @return the locale
         * @internal
         * @deprecated This API is ICU internal only.
         */
        @Deprecated
        public ULocale getLocale() {
            return loc;
        }

        /**
         * Returns the format symbols
         *
         * @return the format symbols
         * @internal
         * @deprecated This API is ICU internal only.
         */
        @Deprecated
        public DateFormatSymbols getDateFormatSymbols() {
            return formatData;
        }
    }

    // -------------------------------------------------------------------------
    // Protected utility methods for use by subclasses.  These are very handy
    // for implementing add, roll, and computeFields.
    // -------------------------------------------------------------------------

    /**
     * Adjust the specified field so that it is within the allowable range for the date to which
     * this calendar is set. For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH
     * DAY_OF_MONTH} field for a calendar set to April 31 would cause it to be set to April 30.
     *
     * <p><b>Subclassing:</b> <br>
     * This utility method is intended for use by subclasses that need to implement their own
     * overrides of {@link #roll roll} and {@link #add add}.
     *
     * <p><b>Note:</b> <code>pinField</code> is implemented in terms of {@link #getActualMinimum
     * getActualMinimum} and {@link #getActualMaximum getActualMaximum}. If either of those methods
     * uses a slow, iterative algorithm for a particular field, it would be unwise to attempt to
     * call <code>pinField</code> for that field. If you really do need to do so, you should
     * override this method to do something more efficient for that field.
     *
     * <p>
     *
     * @param field The calendar field whose value should be pinned.
     * @see #getActualMinimum
     * @see #getActualMaximum
     * @stable ICU 2.0
     */
    protected void pinField(int field) {
        int max = getActualMaximum(field);
        int min = getActualMinimum(field);

        if (fields[field] > max) {
            set(field, max);
        } else if (fields[field] < min) {
            set(field, min);
        }
    }

    /**
     * The year in this calendar is counting from 1 backward if the era is 0.
     *
     * @return The year in era 0 of this calendar is counting backward from 1.
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    protected boolean isEra0CountingBackward() {
        return false;
    }

    /**
     * Returns the week number of a day, within a period. This may be the week number in a year or
     * the week number in a month. Usually this will be a value &gt;= 1, but if some initial days of
     * the period are excluded from week 1, because {@link #getMinimalDaysInFirstWeek
     * getMinimalDaysInFirstWeek} is &gt; 1, then the week number will be zero for those initial
     * days. This method requires the day number and day of week for some known date in the period
     * in order to determine the day of week on the desired day.
     *
     * <p><b>Subclassing:</b> <br>
     * This method is intended for use by subclasses in implementing their {@link #computeTime
     * computeTime} and/or {@link #computeFields computeFields} methods. It is often useful in
     * {@link #getActualMinimum getActualMinimum} and {@link #getActualMaximum getActualMaximum} as
     * well.
     *
     * <p>This variant is handy for computing the week number of some other day of a period (often
     * the first or last day of the period) when its day of the week is not known but the day number
     * and day of week for some other day in the period (e.g. the current date) <em>is</em> known.
     *
     * <p>
     *
     * @param desiredDay The {@link #DAY_OF_YEAR DAY_OF_YEAR} or {@link #DAY_OF_MONTH DAY_OF_MONTH}
     *     whose week number is desired. Should be 1 for the first day of the period.
     * @param dayOfPeriod The {@link #DAY_OF_YEAR DAY_OF_YEAR} or {@link #DAY_OF_MONTH DAY_OF_MONTH}
     *     for a day in the period whose {@link #DAY_OF_WEEK DAY_OF_WEEK} is specified by the <code>
     *     dayOfWeek</code> parameter. Should be 1 for first day of period.
     * @param dayOfWeek The {@link #DAY_OF_WEEK DAY_OF_WEEK} for the day corresponding to the <code>
     *     dayOfPeriod</code> parameter. 1-based with 1=Sunday.
     * @return The week number (one-based), or zero if the day falls before the first week because
     *     {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek} is more than one.
     * @stable ICU 2.0
     */
    protected int weekNumber(int desiredDay, int dayOfPeriod, int dayOfWeek) {
        // Determine the day of the week of the first day of the period
        // in question (either a year or a month).  Zero represents the
        // first day of the week on this calendar.
        int periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek() - dayOfPeriod + 1) % 7;
        if (periodStartDayOfWeek < 0) periodStartDayOfWeek += 7;

        // Compute the week number.  Initially, ignore the first week, which
        // may be fractional (or may not be).  We add periodStartDayOfWeek in
        // order to fill out the first week, if it is fractional.
        int weekNo = (desiredDay + periodStartDayOfWeek - 1) / 7;

        // If the first week is long enough, then count it.  If
        // the minimal days in the first week is one, or if the period start
        // is zero, we always increment weekNo.
        if ((7 - periodStartDayOfWeek) >= getMinimalDaysInFirstWeek()) ++weekNo;

        return weekNo;
    }

    /**
     * Returns the week number of a day, within a period. This may be the week number in a year, or
     * the week number in a month. Usually this will be a value &gt;= 1, but if some initial days of
     * the period are excluded from week 1, because {@link #getMinimalDaysInFirstWeek
     * getMinimalDaysInFirstWeek} is &gt; 1, then the week number will be zero for those initial
     * days. This method requires the day of week for the given date in order to determine the
     * result.
     *
     * <p><b>Subclassing:</b> <br>
     * This method is intended for use by subclasses in implementing their {@link #computeTime
     * computeTime} and/or {@link #computeFields computeFields} methods. It is often useful in
     * {@link #getActualMinimum getActualMinimum} and {@link #getActualMaximum getActualMaximum} as
     * well.
     *
     * <p>
     *
     * @param dayOfPeriod The {@link #DAY_OF_YEAR DAY_OF_YEAR} or {@link #DAY_OF_MONTH DAY_OF_MONTH}
     *     whose week number is desired. Should be 1 for the first day of the period.
     * @param dayOfWeek The {@link #DAY_OF_WEEK DAY_OF_WEEK} for the day corresponding to the <code>
     *     dayOfPeriod</code> parameter. 1-based with 1=Sunday.
     * @return The week number (one-based), or zero if the day falls before the first week because
     *     {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek} is more than one.
     * @stable ICU 2.0
     */
    protected final int weekNumber(int dayOfPeriod, int dayOfWeek) {
        return weekNumber(dayOfPeriod, dayOfPeriod, dayOfWeek);
    }

    // -------------------------------------------------------------------------
    // Constants
    // -------------------------------------------------------------------------

    private static final int FIELD_DIFF_MAX_INT = Integer.MAX_VALUE; // 2147483647

    /**
     * {@icu} Returns the difference between the given time and the time this calendar object is set
     * to. If this calendar is set <em>before</em> the given time, the returned value will be
     * positive. If this calendar is set <em>after</em> the given time, the returned value will be
     * negative. The <code>field</code> parameter specifies the units of the return value. For
     * example, if <code>fieldDifference(when,
     * Calendar.MONTH)</code> returns 3, then this calendar is set to 3 months before <code>when
     * </code>, and possibly some additional time less than one month.
     *
     * <p>As a side effect of this call, this calendar is advanced toward <code>when</code> by the
     * given amount. That is, calling this method has the side effect of calling <code>add(field,
     * n)</code>, where <code>n</code> is the return value.
     *
     * <p>Usage: To use this method, call it first with the largest field of interest, then with
     * progressively smaller fields. For example:
     *
     * <pre>
     * int y = cal.fieldDifference(when, Calendar.YEAR);
     * int m = cal.fieldDifference(when, Calendar.MONTH);
     * int d = cal.fieldDifference(when, Calendar.DATE);</pre>
     *
     * computes the difference between <code>cal</code> and <code>when</code> in years, months, and
     * days.
     *
     * <p>Note: <code>fieldDifference()</code> is <em>asymmetrical</em>. That is, in the following
     * code:
     *
     * <pre>
     * cal.setTime(date1);
     * int m1 = cal.fieldDifference(date2, Calendar.MONTH);
     * int d1 = cal.fieldDifference(date2, Calendar.DATE);
     * cal.setTime(date2);
     * int m2 = cal.fieldDifference(date1, Calendar.MONTH);
     * int d2 = cal.fieldDifference(date1, Calendar.DATE);</pre>
     *
     * one might expect that <code>m1 == -m2 &amp;&amp; d1 == -d2</code>. However, this is not
     * generally the case, because of irregularities in the underlying calendar system (e.g., the
     * Gregorian calendar has a varying number of days per month).
     *
     * @param when the date to compare this calendar's time to
     * @param field the field in which to compute the result
     * @return the difference, either positive or negative, between this calendar's time and <code>
     *     when</code>, in terms of <code>field</code>.
     * @stable ICU 2.0
     */
    public int fieldDifference(Date when, int field) {
        int min = 0;
        long startMs = getTimeInMillis();
        long targetMs = when.getTime();
        // Always add from the start millis.  This accommodates
        // operations like adding years from February 29, 2000 up to
        // February 29, 2004.  If 1, 1, 1, 1 is added to the year
        // field, the DOM gets pinned to 28 and stays there, giving an
        // incorrect DOM difference of 1.  We have to add 1, reset, 2,
        // reset, 3, reset, 4.
        if (startMs < targetMs) {
            int max = 1;
            // Find a value that is too large
            for (; ; ) {
                setTimeInMillis(startMs);
                add(field, max);
                long ms = getTimeInMillis();
                if (ms == targetMs) {
                    return max;
                } else if (ms > targetMs) {
                    break;
                } else if (max < FIELD_DIFF_MAX_INT) {
                    min = max;
                    max <<= 1;
                    if (max < 0) {
                        max = FIELD_DIFF_MAX_INT;
                    }
                } else {
                    // Field difference too large to fit into int
                    throw new RuntimeException();
                }
            }
            // Do a binary search
            while ((max - min) > 1) {
                int t = min + (max - min) / 2; // make sure intermediate values don't exceed
                // FIELD_DIFF_MAX_INT
                setTimeInMillis(startMs);
                add(field, t);
                long ms = getTimeInMillis();
                if (ms == targetMs) {
                    return t;
                } else if (ms > targetMs) {
                    max = t;
                } else {
                    min = t;
                }
            }
        } else if (startMs > targetMs) {
            // Eclipse stated the following is "dead code"
            /*if (false) {
                // This works, and makes the code smaller, but costs
                // an extra object creation and an extra couple cycles
                // of calendar computation.
                setTimeInMillis(targetMs);
                min = -fieldDifference(new Date(startMs), field);
            }*/
            int max = -1;
            // Find a value that is too small
            for (; ; ) {
                setTimeInMillis(startMs);
                add(field, max);
                long ms = getTimeInMillis();
                if (ms == targetMs) {
                    return max;
                } else if (ms < targetMs) {
                    break;
                } else {
                    min = max;
                    max <<= 1;
                    if (max == 0) {
                        // Field difference too large to fit into int
                        throw new RuntimeException();
                    }
                }
            }
            // Do a binary search
            while ((min - max) > 1) {
                int t = min + (max - min) / 2; // make sure intermediate values don't exceed
                // FIELD_DIFF_MAX_INT
                setTimeInMillis(startMs);
                add(field, t);
                long ms = getTimeInMillis();
                if (ms == targetMs) {
                    return t;
                } else if (ms < targetMs) {
                    max = t;
                } else {
                    min = t;
                }
            }
        }
        // Set calendar to end point
        setTimeInMillis(startMs);
        add(field, min);
        return min;
    }

    /**
     * Sets the time zone with the given time zone value.
     *
     * @param value the given time zone.
     * @stable ICU 2.0
     */
    public void setTimeZone(TimeZone value) {
        zone = value;
        /* Recompute the fields from the time using the new zone.  This also
         * works if isTimeSet is false (after a call to set()).  In that case
         * the time will be computed from the fields using the new zone, then
         * the fields will get recomputed from that.  Consider the sequence of
         * calls: cal.setTimeZone(EST); cal.set(HOUR, 1); cal.setTimeZone(PST).
         * Is cal set to 1 o'clock EST or 1 o'clock PST?  Answer: PST.  More
         * generally, a call to setTimeZone() affects calls to set() BEFORE AND
         * AFTER it up to the next call to complete().
         */
        areFieldsSet = false;
    }

    /**
     * Returns the time zone.
     *
     * @return the time zone object associated with this calendar.
     * @stable ICU 2.0
     */
    public TimeZone getTimeZone() {
        return zone;
    }

    /**
     * Specify whether or not date/time interpretation is to be lenient. With lenient
     * interpretation, a date such as "February 942, 1996" will be treated as being equivalent to
     * the 941st day after February 1, 1996. With strict interpretation, such dates will cause an
     * exception to be thrown.
     *
     * @see DateFormat#setLenient
     * @stable ICU 2.0
     */
    public void setLenient(boolean lenient) {
        this.lenient = lenient;
    }

    /**
     * Tell whether date/time interpretation is to be lenient.
     *
     * @stable ICU 2.0
     */
    public boolean isLenient() {
        return lenient;
    }

    /**
     * {@icu}Sets the behavior for handling wall time repeating multiple times at negative time zone
     * offset transitions. For example, 1:30 AM on November 6, 2011 in US Eastern time
     * (America/New_York) occurs twice; 1:30 AM EDT, then 1:30 AM EST one hour later. When <code>
     * WALLTIME_FIRST</code> is used, the wall time 1:30AM in this example will be interpreted as
     * 1:30 AM EDT (first occurrence). When <code>WALLTIME_LAST</code> is used, it will be
     * interpreted as 1:30 AM EST (last occurrence). The default value is <code>WALLTIME_LAST</code>
     * .
     *
     * @param option the behavior for handling repeating wall time, either <code>WALLTIME_FIRST
     *     </code> or <code>WALLTIME_LAST</code>.
     * @throws IllegalArgumentException when <code>option</code> is neither <code>WALLTIME_FIRST
     *     </code> nor <code>WALLTIME_LAST</code>.
     * @see #getRepeatedWallTimeOption()
     * @see #WALLTIME_FIRST
     * @see #WALLTIME_LAST
     * @stable ICU 49
     */
    public void setRepeatedWallTimeOption(int option) {
        if (option != WALLTIME_LAST && option != WALLTIME_FIRST) {
            throw new IllegalArgumentException("Illegal repeated wall time option - " + option);
        }
        repeatedWallTime = option;
    }

    /**
     * {@icu}Gets the behavior for handling wall time repeating multiple times at negative time zone
     * offset transitions.
     *
     * @return the behavior for handling repeating wall time, either <code>WALLTIME_FIRST</code> or
     *     <code>WALLTIME_LAST</code>.
     * @see #setRepeatedWallTimeOption(int)
     * @see #WALLTIME_FIRST
     * @see #WALLTIME_LAST
     * @stable ICU 49
     */
    public int getRepeatedWallTimeOption() {
        return repeatedWallTime;
    }

    /**
     * {@icu}Sets the behavior for handling skipped wall time at positive time zone offset
     * transitions. For example, 2:30 AM on March 13, 2011 in US Eastern time (America/New_York)
     * does not exist because the wall time jump from 1:59 AM EST to 3:00 AM EDT. When <code>
     * WALLTIME_FIRST</code> is used, 2:30 AM is interpreted as 30 minutes before 3:00 AM EDT,
     * therefore, it will be resolved as 1:30 AM EST. When <code>WALLTIME_LAST</code> is used, 2:30
     * AM is interpreted as 31 minutes after 1:59 AM EST, therefore, it will be resolved as 3:30 AM
     * EDT. When <code>WALLTIME_NEXT_VALID</code> is used, 2:30 AM will be resolved as next valid
     * wall time, that is 3:00 AM EDT. The default value is <code>WALLTIME_LAST</code>.
     *
     * <p><b>Note:</b>This option is effective only when this calendar is {@link #isLenient()
     * lenient}. When the calendar is strict, such non-existing wall time will cause an exception.
     *
     * @param option the behavior for handling skipped wall time at positive time zone offset
     *     transitions, one of <code>WALLTIME_FIRST</code>, <code>WALLTIME_LAST</code> and <code>
     *     WALLTIME_NEXT_VALID</code>.
     * @throws IllegalArgumentException when <code>option</code> is not any of <code>WALLTIME_FIRST
     *     </code>, <code>WALLTIME_LAST</code> and <code>WALLTIME_NEXT_VALID</code>.
     * @see #getSkippedWallTimeOption()
     * @see #WALLTIME_FIRST
     * @see #WALLTIME_LAST
     * @see #WALLTIME_NEXT_VALID
     * @stable ICU 49
     */
    public void setSkippedWallTimeOption(int option) {
        if (option != WALLTIME_LAST && option != WALLTIME_FIRST && option != WALLTIME_NEXT_VALID) {
            throw new IllegalArgumentException("Illegal skipped wall time option - " + option);
        }
        skippedWallTime = option;
    }

    /**
     * {@icu}Gets the behavior for handling skipped wall time at positive time zone offset
     * transitions.
     *
     * @return the behavior for handling skipped wall time, one of <code>WALLTIME_FIRST</code>,
     *     <code>WALLTIME_LAST</code> and <code>WALLTIME_NEXT_VALID</code>.
     * @see #setSkippedWallTimeOption(int)
     * @see #WALLTIME_FIRST
     * @see #WALLTIME_LAST
     * @see #WALLTIME_NEXT_VALID
     * @stable ICU 49
     */
    public int getSkippedWallTimeOption() {
        return skippedWallTime;
    }

    /**
     * Sets what the first day of the week is, where 1 = {@link #SUNDAY} and 7 = {@link #SATURDAY}.
     *
     * @param value the given first day of the week, where 1 = {@link #SUNDAY} and 7 = {@link
     *     #SATURDAY}.
     * @stable ICU 2.0
     */
    public void setFirstDayOfWeek(int value) {
        if (firstDayOfWeek != value) {
            if (value < SUNDAY || value > SATURDAY) {
                throw new IllegalArgumentException("Invalid day of week");
            }
            firstDayOfWeek = value;
            areFieldsSet = false;
        }
    }

    /**
     * Returns what the first day of the week is, where 1 = {@link #SUNDAY} and 7 = {@link
     * #SATURDAY}. e.g., Sunday in US, Monday in France
     *
     * @return the first day of the week, where 1 = {@link #SUNDAY} and 7 = {@link #SATURDAY}.
     * @stable ICU 2.0
     */
    public int getFirstDayOfWeek() {
        return firstDayOfWeek;
    }

    /**
     * Sets what the minimal days required in the first week of the year are. For example, if the
     * first week is defined as one that contains the first day of the first month of a year, call
     * the method with value 1. If it must be a full week, use value 7.
     *
     * @param value the given minimal days required in the first week of the year.
     * @stable ICU 2.0
     */
    public void setMinimalDaysInFirstWeek(int value) {
        // Values less than 1 have the same effect as 1; values greater
        // than 7 have the same effect as 7. However, we normalize values
        // so operator== and so forth work.
        if (value < 1) {
            value = 1;
        } else if (value > 7) {
            value = 7;
        }
        if (minimalDaysInFirstWeek != value) {
            minimalDaysInFirstWeek = value;
            areFieldsSet = false;
        }
    }

    /**
     * Returns what the minimal days required in the first week of the year are. That is, if the
     * first week is defined as one that contains the first day of the first month of a year,
     * getMinimalDaysInFirstWeek returns 1. If the minimal days required must be a full week,
     * getMinimalDaysInFirstWeek returns 7.
     *
     * @return the minimal days required in the first week of the year.
     * @stable ICU 2.0
     */
    public int getMinimalDaysInFirstWeek() {
        return minimalDaysInFirstWeek;
    }

    private static final int LIMITS[][] = {
        //    Minimum  Greatest min      Least max   Greatest max
        {
            /*                                                      */
        }, // ERA
        {
            /*                                                      */
        }, // YEAR
        {
            /*                                                      */
        }, // MONTH
        {
            /*                                                      */
        }, // WEEK_OF_YEAR
        {
            /*                                                      */
        }, // WEEK_OF_MONTH
        {
            /*                                                      */
        }, // DAY_OF_MONTH
        {
            /*                                                      */
        }, // DAY_OF_YEAR
        {1, 1, 7, 7}, // DAY_OF_WEEK
        {
            /*                                                      */
        }, // DAY_OF_WEEK_IN_MONTH
        {0, 0, 1, 1}, // AM_PM
        {0, 0, 11, 11}, // HOUR
        {0, 0, 23, 23}, // HOUR_OF_DAY
        {0, 0, 59, 59}, // MINUTE
        {0, 0, 59, 59}, // SECOND
        {0, 0, 999, 999}, // MILLISECOND
        {-24 * ONE_HOUR, -16 * ONE_HOUR, 12 * ONE_HOUR, 30 * ONE_HOUR}, // ZONE_OFFSET
        {0, 0, 2 * ONE_HOUR, 2 * ONE_HOUR}, // DST_OFFSET
        {
            /*                                                      */
        }, // YEAR_WOY
        {1, 1, 7, 7}, // DOW_LOCAL
        {
            /*                                                      */
        }, // EXTENDED_YEAR
        {-0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000}, // JULIAN_DAY
        {0, 0, 24 * ONE_HOUR - 1, 24 * ONE_HOUR - 1}, // MILLISECONDS_IN_DAY
        {0, 0, 1, 1}, // IS_LEAP_MONTH
        {0, 0, 12, 12}, // ORDINAL_MONTH
    };

    /**
     * Subclass API for defining limits of different types. Subclasses must implement this method to
     * return limits for the following fields:
     *
     * <pre>ERA
     * YEAR
     * MONTH
     * WEEK_OF_YEAR
     * WEEK_OF_MONTH
     * DAY_OF_MONTH
     * DAY_OF_YEAR
     * DAY_OF_WEEK_IN_MONTH
     * YEAR_WOY
     * EXTENDED_YEAR</pre>
     *
     * @param field one of the above field numbers
     * @param limitType one of <code>MINIMUM</code>, <code>GREATEST_MINIMUM</code>, <code>
     *     LEAST_MAXIMUM</code>, or <code>MAXIMUM</code>
     * @stable ICU 2.0
     */
    protected abstract int handleGetLimit(int field, int limitType);

    /**
     * Returns a limit for a field.
     *
     * @param field the field, from 0..<code>getFieldCount()-1</code>
     * @param limitType the type specifier for the limit
     * @see #MINIMUM
     * @see #GREATEST_MINIMUM
     * @see #LEAST_MAXIMUM
     * @see #MAXIMUM
     * @stable ICU 2.0
     */
    protected int getLimit(int field, int limitType) {
        switch (field) {
            case DAY_OF_WEEK:
            case AM_PM:
            case HOUR:
            case HOUR_OF_DAY:
            case MINUTE:
            case SECOND:
            case MILLISECOND:
            case ZONE_OFFSET:
            case DST_OFFSET:
            case DOW_LOCAL:
            case JULIAN_DAY:
            case MILLISECONDS_IN_DAY:
            case IS_LEAP_MONTH:
                return LIMITS[field][limitType];

            case WEEK_OF_MONTH:
                {
                    int limit;
                    if (limitType == MINIMUM) {
                        limit = getMinimalDaysInFirstWeek() == 1 ? 1 : 0;
                    } else if (limitType == GREATEST_MINIMUM) {
                        limit = 1;
                    } else {
                        int minDaysInFirst = getMinimalDaysInFirstWeek();
                        int daysInMonth = handleGetLimit(DAY_OF_MONTH, limitType);
                        if (limitType == LEAST_MAXIMUM) {
                            limit = (daysInMonth + (7 - minDaysInFirst)) / 7;
                        } else { // limitType == MAXIMUM
                            limit = (daysInMonth + 6 + (7 - minDaysInFirst)) / 7;
                        }
                    }
                    return limit;
                }
        }
        return handleGetLimit(field, limitType);
    }

    /**
     * Limit type for <code>getLimit()</code> and <code>handleGetLimit()</code> indicating the
     * minimum value that a field can take (least minimum).
     *
     * @see #getLimit
     * @see #handleGetLimit
     * @stable ICU 2.0
     */
    protected static final int MINIMUM = 0;

    /**
     * Limit type for <code>getLimit()</code> and <code>handleGetLimit()</code> indicating the
     * greatest minimum value that a field can take.
     *
     * @see #getLimit
     * @see #handleGetLimit
     * @stable ICU 2.0
     */
    protected static final int GREATEST_MINIMUM = 1;

    /**
     * Limit type for <code>getLimit()</code> and <code>handleGetLimit()</code> indicating the least
     * maximum value that a field can take.
     *
     * @see #getLimit
     * @see #handleGetLimit
     * @stable ICU 2.0
     */
    protected static final int LEAST_MAXIMUM = 2;

    /**
     * Limit type for <code>getLimit()</code> and <code>handleGetLimit()</code> indicating the
     * maximum value that a field can take (greatest maximum).
     *
     * @see #getLimit
     * @see #handleGetLimit
     * @stable ICU 2.0
     */
    protected static final int MAXIMUM = 3;

    /**
     * Returns the minimum value for the given time field. e.g., for Gregorian DAY_OF_MONTH, 1.
     *
     * @param field the given time field.
     * @return the minimum value for the given time field.
     * @stable ICU 2.0
     */
    public final int getMinimum(int field) {
        return getLimit(field, MINIMUM);
    }

    /**
     * Returns the maximum value for the given time field. e.g. for Gregorian DAY_OF_MONTH, 31.
     *
     * @param field the given time field.
     * @return the maximum value for the given time field.
     * @stable ICU 2.0
     */
    public final int getMaximum(int field) {
        return getLimit(field, MAXIMUM);
    }

    /**
     * Returns the highest minimum value for the given field if varies. Otherwise same as
     * getMinimum(). For Gregorian, no difference.
     *
     * @param field the given time field.
     * @return the highest minimum value for the given time field.
     * @stable ICU 2.0
     */
    public final int getGreatestMinimum(int field) {
        return getLimit(field, GREATEST_MINIMUM);
    }

    /**
     * Returns the lowest maximum value for the given field if varies. Otherwise same as
     * getMaximum(). e.g., for Gregorian DAY_OF_MONTH, 28.
     *
     * @param field the given time field.
     * @return the lowest maximum value for the given time field.
     * @stable ICU 2.0
     */
    public final int getLeastMaximum(int field) {
        return getLimit(field, LEAST_MAXIMUM);
    }

    // -------------------------------------------------------------------------
    // Weekend support -- determining which days of the week are the weekend
    // in a given locale
    // -------------------------------------------------------------------------

    /**
     * {@icu} Returns whether the given day of the week is a weekday, a weekend day, or a day that
     * transitions from one to the other, for the locale and calendar system associated with this
     * Calendar (the locale's region is often the most determinant factor). If a transition occurs
     * at midnight, then the days before and after the transition will have the type WEEKDAY or
     * WEEKEND. If a transition occurs at a time other than midnight, then the day of the transition
     * will have the type WEEKEND_ONSET or WEEKEND_CEASE. In this case, the method
     * getWeekendTransition() will return the point of transition.
     *
     * @param dayOfWeek either SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, or SATURDAY
     * @return either WEEKDAY, WEEKEND, WEEKEND_ONSET, or WEEKEND_CEASE
     * @exception IllegalArgumentException if dayOfWeek is not between SUNDAY and SATURDAY,
     *     inclusive
     * @see #WEEKDAY
     * @see #WEEKEND
     * @see #WEEKEND_ONSET
     * @see #WEEKEND_CEASE
     * @see #getWeekendTransition
     * @see #isWeekend(Date)
     * @see #isWeekend()
     * @deprecated ICU 54 use {@link #getWeekDataForRegion(String)}, {@link #getWeekData()}, {@link
     *     #setWeekData(WeekData)}
     */
    @Deprecated
    public int getDayOfWeekType(int dayOfWeek) {
        if (dayOfWeek < SUNDAY || dayOfWeek > SATURDAY) {
            throw new IllegalArgumentException("Invalid day of week");
        }
        if (weekendOnset == weekendCease) {
            if (dayOfWeek != weekendOnset) return WEEKDAY;
            return (weekendOnsetMillis == 0) ? WEEKEND : WEEKEND_ONSET;
        }
        if (weekendOnset < weekendCease) {
            if (dayOfWeek < weekendOnset || dayOfWeek > weekendCease) {
                return WEEKDAY;
            }
        } else {
            if (dayOfWeek > weekendCease && dayOfWeek < weekendOnset) {
                return WEEKDAY;
            }
        }
        if (dayOfWeek == weekendOnset) {
            return (weekendOnsetMillis == 0) ? WEEKEND : WEEKEND_ONSET;
        }
        if (dayOfWeek == weekendCease) {
            return (weekendCeaseMillis >= 86400000) ? WEEKEND : WEEKEND_CEASE;
        }
        return WEEKEND;
    }

    /**
     * {@icu} Returns the time during the day at which the weekend begins or end in this calendar
     * system. If getDayOfWeekType(dayOfWeek) == WEEKEND_ONSET return the time at which the weekend
     * begins. If getDayOfWeekType(dayOfWeek) == WEEKEND_CEASE return the time at which the weekend
     * ends. If getDayOfWeekType(dayOfWeek) has some other value, then throw an exception.
     *
     * @param dayOfWeek either SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, or SATURDAY
     * @return the milliseconds after midnight at which the weekend begins or ends
     * @exception IllegalArgumentException if dayOfWeek is not WEEKEND_ONSET or WEEKEND_CEASE
     * @see #getDayOfWeekType
     * @see #isWeekend(Date)
     * @see #isWeekend()
     * @deprecated ICU 54 use {@link #getWeekDataForRegion(String)}, {@link #getWeekData()}, {@link
     *     #setWeekData(WeekData)}
     */
    @Deprecated
    public int getWeekendTransition(int dayOfWeek) {
        if (dayOfWeek == weekendOnset) {
            return weekendOnsetMillis;
        } else if (dayOfWeek == weekendCease) {
            return weekendCeaseMillis;
        }
        throw new IllegalArgumentException("Not weekend transition day");
    }

    /**
     * {@icu} Returns true if the given date and time is in the weekend in this calendar system.
     * Equivalent to calling setTime() followed by isWeekend(). Note: This method changes the time
     * this calendar is set to.
     *
     * @param date the date and time
     * @return true if the given date and time is part of the weekend
     * @see #getDayOfWeekType
     * @see #getWeekendTransition
     * @see #isWeekend()
     * @stable ICU 2.0
     */
    public boolean isWeekend(Date date) {
        setTime(date);
        return isWeekend();
    }

    /**
     * {@icu} Returns true if this Calendar's current date and time is in the weekend in this
     * calendar system.
     *
     * @return true if the given date and time is part of the weekend
     * @see #getDayOfWeekType
     * @see #getWeekendTransition
     * @see #isWeekend(Date)
     * @stable ICU 2.0
     */
    public boolean isWeekend() {
        int dow = get(DAY_OF_WEEK);
        int dowt = getDayOfWeekType(dow);
        switch (dowt) {
            case WEEKDAY:
                return false;
            case WEEKEND:
                return true;
            default: // That is, WEEKEND_ONSET or WEEKEND_CEASE
                // Use internalGet() because the above call to get() populated
                // all fields.
                // [Note: There should be a better way to get millis in day.
                //  For ICU4J, submit request for a MILLIS_IN_DAY field
                //  and a DAY_NUMBER field (could be Julian day #). - aliu]
                int millisInDay =
                        internalGet(MILLISECOND)
                                + 1000
                                        * (internalGet(SECOND)
                                                + 60
                                                        * (internalGet(MINUTE)
                                                                + 60 * internalGet(HOUR_OF_DAY)));
                int transition = getWeekendTransition(dow);
                return (dowt == WEEKEND_ONSET)
                        ? (millisInDay >= transition)
                        : (millisInDay < transition);
        }
        // (We can never reach this point.)
    }

    // -------------------------------------------------------------------------
    // End of weekend support
    // -------------------------------------------------------------------------

    /**
     * Overrides Cloneable
     *
     * @stable ICU 2.0
     */
    @Override
    public Calendar clone() {
        try {
            Calendar other = (Calendar) super.clone();

            other.fields = new int[fields.length];
            other.stamp = new byte[fields.length];
            System.arraycopy(this.fields, 0, other.fields, 0, fields.length);
            System.arraycopy(this.stamp, 0, other.stamp, 0, fields.length);
            other.zone = zone.clone();
            return other;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new ICUCloneNotSupportedException(e);
        }
    }

    /**
     * Returns a string representation of this calendar. This method is intended to be used only for
     * debugging purposes, and the format of the returned string may vary between implementations.
     * The returned string may be empty but may not be <code>null</code>.
     *
     * @return a string representation of this calendar.
     * @stable ICU 2.0
     */
    @Override
    public String toString() {
        StringBuilder buffer = new StringBuilder();
        buffer.append(getClass().getName());
        buffer.append("[time=");
        buffer.append(isTimeSet ? String.valueOf(time) : "?");
        buffer.append(",areFieldsSet=");
        buffer.append(areFieldsSet);
        buffer.append(",areAllFieldsSet=");
        buffer.append(areAllFieldsSet);
        buffer.append(",lenient=");
        buffer.append(lenient);
        buffer.append(",zone=");
        buffer.append(zone);
        buffer.append(",firstDayOfWeek=");
        buffer.append(firstDayOfWeek);
        buffer.append(",minimalDaysInFirstWeek=");
        buffer.append(minimalDaysInFirstWeek);
        buffer.append(",repeatedWallTime=");
        buffer.append(repeatedWallTime);
        buffer.append(",skippedWallTime=");
        buffer.append(skippedWallTime);
        for (int i = 0; i < fields.length; ++i) {
            buffer.append(',').append(fieldName(i)).append('=');
            buffer.append(isSet(i) ? String.valueOf(fields[i]) : "?");
        }
        buffer.append(']');
        return buffer.toString();
    }

    /**
     * Simple, immutable struct-like class for access to the CLDR week data.
     *
     * @stable ICU 54
     */
    public static final class WeekData {
        /**
         * the first day of the week, where 1 = {@link #SUNDAY} and 7 = {@link #SATURDAY}
         *
         * @stable ICU 54
         */
        public final int firstDayOfWeek;

        /**
         * the minimal number of days in the first week
         *
         * @stable ICU 54
         */
        public final int minimalDaysInFirstWeek;

        /**
         * the onset day, where 1 = {@link #SUNDAY} and 7 = {@link #SATURDAY}
         *
         * @stable ICU 54
         */
        public final int weekendOnset;

        /**
         * the onset time in millis during the onset day
         *
         * @stable ICU 54
         */
        public final int weekendOnsetMillis;

        /**
         * the cease day, where 1 = {@link #SUNDAY} and 7 = {@link #SATURDAY}
         *
         * @stable ICU 54
         */
        public final int weekendCease;

        /**
         * the cease time in millis during the cease day. Exclusive, so the max is 24:00:00.000.
         * Note that this will format as 00:00 the next day.
         *
         * @stable ICU 54
         */
        public final int weekendCeaseMillis;

        /**
         * Constructor
         *
         * @param fdow the first day of the week, where 1 = {@link #SUNDAY} and 7 = {@link
         *     #SATURDAY}
         * @param mdifw the minimal number of days in the first week
         * @param weekendOnset the onset day, where 1 = Sunday and 7 = Saturday
         * @param weekendOnsetMillis the onset time in millis during the onset day
         * @param weekendCease the cease day, where 1 = Sunday and 7 = Saturday
         * @param weekendCeaseMillis the cease time in millis during the cease day.
         * @stable ICU 54
         */
        public WeekData(
                int fdow,
                int mdifw,
                int weekendOnset,
                int weekendOnsetMillis,
                int weekendCease,
                int weekendCeaseMillis) {
            this.firstDayOfWeek = fdow;
            this.minimalDaysInFirstWeek = mdifw;
            this.weekendOnset = weekendOnset;
            this.weekendOnsetMillis = weekendOnsetMillis;
            this.weekendCease = weekendCease;
            this.weekendCeaseMillis = weekendCeaseMillis;
        }

        /**
         * {@inheritDoc}
         *
         * @stable ICU 54
         */
        @Override
        public int hashCode() {
            return ((((firstDayOfWeek * 37 + minimalDaysInFirstWeek) * 37 + weekendOnset) * 37
                                                    + weekendOnsetMillis)
                                            * 37
                                    + weekendCease)
                            * 37
                    + weekendCeaseMillis;
        }

        /**
         * {@inheritDoc}
         *
         * @stable ICU 54
         */
        @Override
        public boolean equals(Object other) {
            if (this == other) {
                return true;
            }
            if (!(other instanceof WeekData)) {
                return false;
            }
            WeekData that = (WeekData) other;
            return firstDayOfWeek == that.firstDayOfWeek
                    && minimalDaysInFirstWeek == that.minimalDaysInFirstWeek
                    && weekendOnset == that.weekendOnset
                    && weekendOnsetMillis == that.weekendOnsetMillis
                    && weekendCease == that.weekendCease
                    && weekendCeaseMillis == that.weekendCeaseMillis;
        }

        /**
         * {@inheritDoc}
         *
         * @stable ICU 54
         */
        @Override
        public String toString() {
            return "{"
                    + firstDayOfWeek
                    + ", "
                    + minimalDaysInFirstWeek
                    + ", "
                    + weekendOnset
                    + ", "
                    + weekendOnsetMillis
                    + ", "
                    + weekendCease
                    + ", "
                    + weekendCeaseMillis
                    + "}";
        }
    }

    /**
     * {@icu} Return simple, immutable struct-like class for access to the CLDR week data.
     *
     * @param region The input region. The results are undefined if the region code is not valid.
     * @return the WeekData for the input region. It is never null.
     * @stable ICU 54
     */
    public static WeekData getWeekDataForRegion(String region) {
        return WEEK_DATA_CACHE.createInstance(region, region);
    }

    /**
     * {@icu} Return simple, immutable struct-like class for access to the week data in this
     * calendar.
     *
     * @return the WeekData for this calendar.
     * @stable ICU 54
     */
    public WeekData getWeekData() {
        return new WeekData(
                firstDayOfWeek,
                minimalDaysInFirstWeek,
                weekendOnset,
                weekendOnsetMillis,
                weekendCease,
                weekendCeaseMillis);
    }

    /**
     * {@icu} Set data in this calendar based on the WeekData input.
     *
     * @param wdata The week data to use
     * @return this, for chaining
     * @stable ICU 54
     */
    public Calendar setWeekData(WeekData wdata) {
        setFirstDayOfWeek(wdata.firstDayOfWeek);
        setMinimalDaysInFirstWeek(wdata.minimalDaysInFirstWeek);

        weekendOnset = wdata.weekendOnset;
        weekendOnsetMillis = wdata.weekendOnsetMillis;
        weekendCease = wdata.weekendCease;
        weekendCeaseMillis = wdata.weekendCeaseMillis;
        return this;
    }

    private static WeekData getWeekDataForRegionInternal(String region) {
        if (region == null) {
            region = "001";
        }

        UResourceBundle rb =
                UResourceBundle.getBundleInstance(
                        ICUData.ICU_BASE_NAME,
                        "supplementalData",
                        ICUResourceBundle.ICU_DATA_CLASS_LOADER);
        UResourceBundle weekDataInfo = rb.get("weekData");
        UResourceBundle weekDataBundle = null;

        try {
            weekDataBundle = weekDataInfo.get(region);
        } catch (MissingResourceException mre) {
            if (!region.equals("001")) {
                // use "001" as fallback
                weekDataBundle = weekDataInfo.get("001");
            } else {
                throw mre;
            }
        }

        int[] wdi = weekDataBundle.getIntVector();
        return new WeekData(wdi[0], wdi[1], wdi[2], wdi[3], wdi[4], wdi[5]);
    }

    /*
     * Cache to hold week data by region
     */
    private static class WeekDataCache extends SoftCache<String, WeekData, String> {

        /* (non-Javadoc)
         * @see com.ibm.icu.impl.CacheBase#createInstance(java.lang.Object, java.lang.Object)
         */
        @Override
        protected WeekData createInstance(String key, String data) {
            return getWeekDataForRegionInternal(key);
        }
    }

    private static final WeekDataCache WEEK_DATA_CACHE = new WeekDataCache();

    /*
     * Set this calendar to contain week and week data for the given region.
     */
    private void setWeekData(String region) {
        if (region == null) {
            region = "001";
        }
        WeekData wdata = WEEK_DATA_CACHE.getInstance(region, region);
        setWeekData(wdata);
    }

    /**
     * Recompute the time and update the status fields isTimeSet and areFieldsSet. Callers should
     * check isTimeSet and only call this method if isTimeSet is false.
     */
    private void updateTime() {
        computeTime();
        // If we are lenient, we need to recompute the fields to normalize
        // the values.  Also, if we haven't set all the fields yet (i.e.,
        // in a newly-created object), we need to fill in the fields. [LIU]
        if (isLenient() || !areAllFieldsSet) areFieldsSet = false;
        isTimeSet = true;
        areFieldsVirtuallySet = false;
    }

    /** Save the state of this object to a stream (i.e., serialize it). */
    private void writeObject(ObjectOutputStream stream) throws IOException {
        // Try to compute the time correctly, for the future (stream
        // version 2) in which we don't write out fields[] or isSet[].
        if (!isTimeSet) {
            try {
                updateTime();
            } catch (IllegalArgumentException e) {
            }
        }

        // Write out the 1.1 FCS object.
        stream.defaultWriteObject();
    }

    /** Reconstitute this object from a stream (i.e., deserialize it). */
    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {

        stream.defaultReadObject();

        initInternal();

        isTimeSet = true;
        areFieldsSet = areAllFieldsSet = false;
        areFieldsVirtuallySet = true; // cause fields to be recalculated if requested.
        nextStamp = MINIMUM_USER_STAMP;
    }

    // ----------------------------------------------------------------------
    // Time -> Fields
    // ----------------------------------------------------------------------

    /**
     * Converts the current millisecond time value <code>time</code> to field values in <code>
     * fields[]</code>. This synchronizes the time field values with a new time that is set for the
     * calendar. The time is <em>not</em> recomputed first; to recompute the time, then the fields,
     * call the <code>complete</code> method.
     *
     * @see #complete
     * @stable ICU 2.0
     */
    protected void computeFields() {
        int offsets[] = new int[2];
        getTimeZone().getOffset(time, false, offsets);
        long localMillis = time + offsets[0] + offsets[1];

        // Mark fields as set.  Do this before calling handleComputeFields().
        int mask = internalSetMask;
        for (int i = 0; i < fields.length; ++i) {
            if ((mask & 1) == 0) {
                stamp[i] = INTERNALLY_SET;
            } else {
                stamp[i] = UNSET;
            }
            mask >>= 1;
        }

        // We used to check for and correct extreme millis values (near
        // Long.MIN_VALUE or Long.MAX_VALUE) here.  Such values would cause
        // overflows from positive to negative (or vice versa) and had to
        // be manually tweaked.  We no longer need to do this because we
        // have limited the range of supported dates to those that have a
        // Julian day that fits into an int.  This allows us to implement a
        // JULIAN_DAY field and also removes some inelegant code. - Liu
        // 11/6/00

        long days = floorDivide(localMillis, ONE_DAY);

        fields[JULIAN_DAY] = (int) days + EPOCH_JULIAN_DAY;

        computeGregorianAndDOWFields(fields[JULIAN_DAY]);

        // Call framework method to have subclass compute its fields.
        // These must include, at a minimum, MONTH, DAY_OF_MONTH,
        // EXTENDED_YEAR, YEAR, DAY_OF_YEAR.  This method will call internalSet(),
        // which will update stamp[].
        handleComputeFields(fields[JULIAN_DAY]);

        // Compute week-related fields, based on the subclass-computed
        // fields computed by handleComputeFields().
        computeWeekFields();

        // Compute time-related fields.  These are independent of the date and
        // of the subclass algorithm.  They depend only on the local zone
        // wall milliseconds in day.
        int millisInDay = (int) (localMillis - (days * ONE_DAY));
        fields[MILLISECONDS_IN_DAY] = millisInDay;
        fields[MILLISECOND] = millisInDay % 1000;
        millisInDay /= 1000;
        fields[SECOND] = millisInDay % 60;
        millisInDay /= 60;
        fields[MINUTE] = millisInDay % 60;
        millisInDay /= 60;
        fields[HOUR_OF_DAY] = millisInDay;
        fields[AM_PM] = millisInDay / 12; // Assume AM == 0
        fields[HOUR] = millisInDay % 12;
        fields[ZONE_OFFSET] = offsets[0];
        fields[DST_OFFSET] = offsets[1];
    }

    /**
     * Compute the Gregorian calendar year, month, and day of month from the given Julian day. These
     * values are not stored in fields, but in member variables gregorianXxx. Also compute the
     * DAY_OF_WEEK and DOW_LOCAL fields.
     */
    private final void computeGregorianAndDOWFields(int julianDay) {
        computeGregorianFields(julianDay);

        // Compute day of week: JD 0 = Monday
        int dow = fields[DAY_OF_WEEK] = julianDayToDayOfWeek(julianDay);

        // Calculate 1-based localized day of week
        int dowLocal = dow - getFirstDayOfWeek() + 1;
        if (dowLocal < 1) {
            dowLocal += 7;
        }
        fields[DOW_LOCAL] = dowLocal;
    }

    /**
     * Compute the Gregorian calendar year, month, and day of month from the Julian day. These
     * values are not stored in fields, but in member variables gregorianXxx. They are used for time
     * zone computations and by subclasses that are Gregorian derivatives. Subclasses may call this
     * method to perform a Gregorian calendar millis-&gt;fields computation. To perform a Gregorian
     * calendar fields-&gt;millis computation, call computeGregorianMonthStart().
     *
     * @see #computeGregorianMonthStart
     * @stable ICU 2.0
     */
    protected final void computeGregorianFields(int julianDay) {
        int[] gregorian = Grego.dayToFields(julianDay - EPOCH_JULIAN_DAY, null);
        gregorianYear = gregorian[0];
        gregorianMonth = gregorian[1];
        gregorianDayOfMonth = gregorian[2];
        gregorianDayOfYear = gregorian[4];
    }

    /**
     * Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH, DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL
     * from EXTENDED_YEAR, YEAR, DAY_OF_WEEK, and DAY_OF_YEAR. The latter fields are computed by the
     * subclass based on the calendar system.
     *
     * <p>The YEAR_WOY field is computed simplistically. It is equal to YEAR most of the time, but
     * at the year boundary it may be adjusted to YEAR-1 or YEAR+1 to reflect the overlap of a week
     * into an adjacent year. In this case, a simple increment or decrement is performed on YEAR,
     * even though this may yield an invalid YEAR value. For instance, if the YEAR is part of a
     * calendar system with an N-year cycle field CYCLE, then incrementing the YEAR may involve
     * incrementing CYCLE and setting YEAR back to 0 or 1. This is not handled by this code, and in
     * fact cannot be simply handled without having subclasses define an entire parallel set of
     * fields for fields larger than or equal to a year. This additional complexity is not
     * warranted, since the intention of the YEAR_WOY field is to support ISO 8601 notation, so it
     * will typically be used with a proleptic Gregorian calendar, which has no field larger than a
     * year.
     */
    private final void computeWeekFields() {
        int eyear = fields[EXTENDED_YEAR];
        int dayOfWeek = fields[DAY_OF_WEEK];
        int dayOfYear = fields[DAY_OF_YEAR];

        // WEEK_OF_YEAR start
        // Compute the week of the year.  For the Gregorian calendar, valid week
        // numbers run from 1 to 52 or 53, depending on the year, the first day
        // of the week, and the minimal days in the first week.  For other
        // calendars, the valid range may be different -- it depends on the year
        // length.  Days at the start of the year may fall into the last week of
        // the previous year; days at the end of the year may fall into the
        // first week of the next year.  ASSUME that the year length is less than
        // 7000 days.
        int yearOfWeekOfYear = eyear;
        int relDow = (dayOfWeek + 7 - getFirstDayOfWeek()) % 7; // 0..6
        int relDowJan1 = (dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek()) % 7; // 0..6
        int woy = (dayOfYear - 1 + relDowJan1) / 7; // 0..53
        if ((7 - relDowJan1) >= getMinimalDaysInFirstWeek()) {
            ++woy;
        }

        // Adjust for weeks at the year end that overlap into the previous or
        // next calendar year.
        if (woy == 0) {
            // We are the last week of the previous year.
            // Check to see if we are in the last week; if so, we need
            // to handle the case in which we are the first week of the
            // next year.

            int prevDoy = dayOfYear + handleGetYearLength(eyear - 1);
            woy = weekNumber(prevDoy, dayOfWeek);
            yearOfWeekOfYear--;
        } else {
            int lastDoy = handleGetYearLength(eyear);
            // Fast check: For it to be week 1 of the next year, the DOY
            // must be on or after L-5, where L is yearLength(), then it
            // cannot possibly be week 1 of the next year:
            //          L-5                  L
            // doy: 359 360 361 362 363 364 365 001
            // dow:      1   2   3   4   5   6   7
            if (dayOfYear >= (lastDoy - 5)) {
                int lastRelDow = (relDow + lastDoy - dayOfYear) % 7;
                if (lastRelDow < 0) {
                    lastRelDow += 7;
                }
                if (((6 - lastRelDow) >= getMinimalDaysInFirstWeek())
                        && ((dayOfYear + 7 - relDow) > lastDoy)) {
                    woy = 1;
                    yearOfWeekOfYear++;
                }
            }
        }
        fields[WEEK_OF_YEAR] = woy;
        fields[YEAR_WOY] = yearOfWeekOfYear;
        // WEEK_OF_YEAR end

        int dayOfMonth = fields[DAY_OF_MONTH];
        fields[WEEK_OF_MONTH] = weekNumber(dayOfMonth, dayOfWeek);
        fields[DAY_OF_WEEK_IN_MONTH] = (dayOfMonth - 1) / 7 + 1;
    }

    // ----------------------------------------------------------------------
    // Fields -> Time
    // ----------------------------------------------------------------------

    /**
     * Value to OR against resolve table field values for remapping.
     *
     * @see #resolveFields
     * @stable ICU 2.0
     */
    protected static final int RESOLVE_REMAP = 32;

    // A power of 2 greater than or equal to MAX_FIELD_COUNT

    // Default table for day in year
    static final int[][][] DATE_PRECEDENCE = {
        {
            {DAY_OF_MONTH},
            {WEEK_OF_YEAR, DAY_OF_WEEK},
            {WEEK_OF_MONTH, DAY_OF_WEEK},
            {DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK},
            {WEEK_OF_YEAR, DOW_LOCAL},
            {WEEK_OF_MONTH, DOW_LOCAL},
            {DAY_OF_WEEK_IN_MONTH, DOW_LOCAL},
            {DAY_OF_YEAR},
            {RESOLVE_REMAP | DAY_OF_MONTH, YEAR}, // if YEAR is set over YEAR_WOY use DAY_OF_MONTH
            {
                RESOLVE_REMAP | WEEK_OF_YEAR, YEAR_WOY
            }, // if YEAR_WOY is set,  calc based on WEEK_OF_YEAR
        },
        {
            {WEEK_OF_YEAR},
            {WEEK_OF_MONTH},
            {DAY_OF_WEEK_IN_MONTH},
            {RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK},
            {RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DOW_LOCAL},
        },
    };

    static final int[][][] DOW_PRECEDENCE = {
        {
            {DAY_OF_WEEK}, {DOW_LOCAL},
        },
    };

    static final int[][][] MONTH_PRECEDENCE = {
        {
            {MONTH}, {ORDINAL_MONTH},
        },
    };

    /**
     * Given a precedence table, return the newest field combination in the table, or -1 if none is
     * found.
     *
     * <p>The precedence table is a 3-dimensional array of integers. It may be thought of as an
     * array of groups. Each group is an array of lines. Each line is an array of field numbers.
     * Within a line, if all fields are set, then the time stamp of the line is taken to be the
     * stamp of the most recently set field. If any field of a line is unset, then the line fails to
     * match. Within a group, the line with the newest time stamp is selected. The first field of
     * the line is returned to indicate which line matched.
     *
     * <p>In some cases, it may be desirable to map a line to field that whose stamp is NOT
     * examined. For example, if the best field is DAY_OF_WEEK then the DAY_OF_WEEK_IN_MONTH
     * algorithm may be used. In order to do this, insert the value <code>REMAP_RESOLVE | F</code>
     * at the start of the line, where <code>F</code> is the desired return field value. This field
     * will NOT be examined; it only determines the return value if the other fields in the line are
     * the newest.
     *
     * <p>If all lines of a group contain at least one unset field, then no line will match, and the
     * group as a whole will fail to match. In that case, the next group will be processed. If all
     * groups fail to match, then -1 is returned.
     *
     * @stable ICU 2.0
     */
    protected int resolveFields(int[][][] precedenceTable) {
        int bestField = -1;
        int tempBestField;
        for (int g = 0; g < precedenceTable.length && bestField < 0; ++g) {
            int[][] group = precedenceTable[g];
            int bestStamp = UNSET;
            linesInGroup:
            for (int l = 0; l < group.length; ++l) {
                int[] line = group[l];
                int lineStamp = UNSET;
                // Skip over first entry if it is negative
                for (int i = (line[0] >= RESOLVE_REMAP) ? 1 : 0; i < line.length; ++i) {
                    int s = stamp[line[i]];
                    // If any field is unset then don't use this line
                    if (s == UNSET) {
                        continue linesInGroup;
                    } else {
                        lineStamp = Math.max(lineStamp, s);
                    }
                }
                // Record new maximum stamp & field no.
                if (lineStamp > bestStamp) {
                    tempBestField = line[0]; // First field refers to entire line
                    if (tempBestField >= RESOLVE_REMAP) {
                        tempBestField &= (RESOLVE_REMAP - 1);
                        // This check is needed to resolve some issues with UCAL_YEAR precedence
                        // mapping
                        if (tempBestField != DATE
                                || (stamp[WEEK_OF_MONTH] < stamp[tempBestField])) {
                            bestField = tempBestField;
                        }
                    } else {
                        bestField = tempBestField;
                    }

                    if (bestField == tempBestField) {
                        bestStamp = lineStamp;
                    }
                }
            }
        }
        return (bestField >= RESOLVE_REMAP) ? (bestField & (RESOLVE_REMAP - 1)) : bestField;
    }

    /**
     * Returns the newest stamp of a given range of fields.
     *
     * @stable ICU 2.0
     */
    protected int newestStamp(int first, int last, int bestStampSoFar) {
        int bestStamp = bestStampSoFar;
        for (int i = first; i <= last; ++i) {
            if (stamp[i] > bestStamp) {
                bestStamp = stamp[i];
            }
        }
        return bestStamp;
    }

    /**
     * Returns the timestamp of a field.
     *
     * @stable ICU 2.0
     */
    protected final int getStamp(int field) {
        return stamp[field];
    }

    /**
     * Returns the field that is newer, either defaultField, or alternateField. If neither is newer
     * or neither is set, return defaultField.
     *
     * @stable ICU 2.0
     */
    protected int newerField(int defaultField, int alternateField) {
        if (stamp[alternateField] > stamp[defaultField]) {
            return alternateField;
        }
        return defaultField;
    }

    /**
     * Ensure that each field is within its valid range by calling {@link #validateField(int)} on
     * each field that has been set. This method should only be called if this calendar is not
     * lenient.
     *
     * @see #isLenient
     * @see #validateField(int)
     * @stable ICU 2.0
     */
    protected void validateFields() {
        for (int field = 0; field < fields.length; field++) {
            if (stamp[field] >= MINIMUM_USER_STAMP) {
                validateField(field);
            }
        }
    }

    /**
     * Validate a single field of this calendar. Subclasses should override this method to validate
     * any calendar-specific fields. Generic fields can be handled by <code>Calendar.validateField()
     * </code>.
     *
     * @see #validateField(int, int, int)
     * @stable ICU 2.0
     */
    protected void validateField(int field) {
        int y;
        switch (field) {
            case DAY_OF_MONTH:
                y = handleGetExtendedYear();
                validateField(field, 1, handleGetMonthLength(y, internalGetMonth()));
                break;
            case DAY_OF_YEAR:
                y = handleGetExtendedYear();
                validateField(field, 1, handleGetYearLength(y));
                break;
            case DAY_OF_WEEK_IN_MONTH:
                if (internalGet(field) == 0) {
                    throw new IllegalArgumentException("DAY_OF_WEEK_IN_MONTH cannot be zero");
                }
                validateField(field, getMinimum(field), getMaximum(field));
                break;
            default:
                validateField(field, getMinimum(field), getMaximum(field));
                break;
        }
    }

    /**
     * Validate a single field of this calendar given its minimum and maximum allowed value. If the
     * field is out of range, throw a descriptive <code>IllegalArgumentException</code>. Subclasses
     * may use this method in their implementation of {@link #validateField(int)}.
     *
     * @stable ICU 2.0
     */
    protected final void validateField(int field, int min, int max) {
        int value = fields[field];
        if (value < min || value > max) {
            throw new IllegalArgumentException(
                    fieldName(field) + '=' + value + ", valid range=" + min + ".." + max);
        }
    }

    /**
     * Converts the current field values in <code>fields[]</code> to the millisecond time value
     * <code>time</code>.
     *
     * @stable ICU 2.0
     */
    protected void computeTime() {
        if (!isLenient()) {
            validateFields();
        }

        // Compute the Julian day
        int julianDay = computeJulianDay();

        long millis = julianDayToMillis(julianDay);

        long millisInDay;

        // We only use MILLISECONDS_IN_DAY if it has been set by the user.
        // This makes it possible for the caller to set the calendar to a
        // time and call clear(MONTH) to reset the MONTH to January.  This
        // is legacy behavior.  Without this, clear(MONTH) has no effect,
        // since the internally set JULIAN_DAY is used.
        if (stamp[MILLISECONDS_IN_DAY] >= MINIMUM_USER_STAMP
                && newestStamp(AM_PM, MILLISECOND, UNSET) <= stamp[MILLISECONDS_IN_DAY]) {
            millisInDay = internalGet(MILLISECONDS_IN_DAY);
        } else {
            int hour = Math.abs(internalGet(HOUR_OF_DAY));
            hour = Math.max(hour, Math.abs(internalGet(HOUR)));
            // if hour field value is greater than 596, then the
            // milliseconds value exceeds integer range, hence
            // using a conservative estimate of 548, we invoke
            // the long return version of the compute millis method if
            // the hour value exceeds 548
            if (hour > MAX_HOURS) {
                millisInDay = computeMillisInDayLong();
            } else {
                millisInDay = computeMillisInDay();
            }
        }

        if (stamp[ZONE_OFFSET] >= MINIMUM_USER_STAMP || stamp[DST_OFFSET] >= MINIMUM_USER_STAMP) {
            time = millis + millisInDay - (internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET));
        } else {
            // Compute the time zone offset and DST offset.  There are two potential
            // ambiguities here.  We'll assume a 2:00 am (wall time) switchover time
            // for discussion purposes here.
            //
            // 1. The positive offset change such as transition into DST.
            //    Here, a designated time of 2:00 am - 2:59 am does not actually exist.
            //    For this case, skippedWallTime option specifies the behavior.
            //    For example, 2:30 am is interpreted as;
            //      - WALLTIME_LAST(default): 3:30 am (DST) (interpreting 2:30 am as 31 minutes
            // after 1:59 am (STD))
            //      - WALLTIME_FIRST: 1:30 am (STD) (interpreting 2:30 am as 30 minutes before 3:00
            // am (DST))
            //      - WALLTIME_NEXT_VALID: 3:00 am (DST) (next valid time after 2:30 am on a wall
            // clock)
            // 2. The negative offset change such as transition out of DST.
            //    Here, a designated time of 1:00 am - 1:59 am can be in standard or DST.  Both are
            // valid
            //    representations (the rep jumps from 1:59:59 DST to 1:00:00 Std).
            //    For this case, repeatedWallTime option specifies the behavior.
            //    For example, 1:30 am is interpreted as;
            //      - WALLTIME_LAST(default): 1:30 am (STD) - latter occurrence
            //      - WALLTIME_FIRST: 1:30 am (DST) - former occurrence
            //
            // In addition to above, when calendar is strict (not default), wall time falls into
            // the skipped time range will be processed as an error case.
            //
            // These special cases are mostly handled in #computeZoneOffset(long), except
            // WALLTIME_NEXT_VALID
            // at positive offset change. The protected method computeZoneOffset(long) is exposed to
            // Calendar
            // subclass implementations and marked as @stable. Strictly speaking,
            // WALLTIME_NEXT_VALID
            // should be also handled in the same place, but we cannot change the code flow without
            // deprecating
            // the protected method.
            //
            // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
            // or DST_OFFSET fields; then we use those fields.

            if (!lenient || skippedWallTime == WALLTIME_NEXT_VALID) {
                // When strict, invalidate a wall time falls into a skipped wall time range.
                // When lenient and skipped wall time option is WALLTIME_NEXT_VALID,
                // the result time will be adjusted to the next valid time (on wall clock).
                int zoneOffset = computeZoneOffset(millis, millisInDay);
                long tmpTime = millis + millisInDay - zoneOffset;

                int zoneOffset1 = zone.getOffset(tmpTime);

                // zoneOffset != zoneOffset1 only when the given wall time fall into
                // a skipped wall time range caused by positive zone offset transition.
                if (zoneOffset != zoneOffset1) {
                    if (!lenient) {
                        throw new IllegalArgumentException(
                                "The specified wall time does not exist due to time zone offset transition.");
                    }

                    assert skippedWallTime == WALLTIME_NEXT_VALID : skippedWallTime;
                    // Adjust time to the next valid wall clock time.
                    // At this point, tmpTime is on or after the zone offset transition causing
                    // the skipped time range.
                    Long immediatePrevTransition = getImmediatePreviousZoneTransition(tmpTime);
                    if (immediatePrevTransition == null) {
                        throw new RuntimeException(
                                "Could not locate a time zone transition before " + tmpTime);
                    }
                    time = immediatePrevTransition;
                } else {
                    time = tmpTime;
                }
            } else {
                time = millis + millisInDay - computeZoneOffset(millis, millisInDay);
            }
        }
    }

    /**
     * Find the previous zone transition near the given time.
     *
     * @param base The base time, inclusive.
     * @return The time of the previous transition, or null if not found.
     */
    private Long getImmediatePreviousZoneTransition(long base) {
        Long transitionTime = null;

        if (zone instanceof BasicTimeZone) {
            TimeZoneTransition transition =
                    ((BasicTimeZone) zone).getPreviousTransition(base, true);
            if (transition != null) {
                transitionTime = transition.getTime();
            }
        } else {
            // Usually, it is enough to check past one hour because such transition is most
            // likely +1 hour shift. However, there is an example jumped +24 hour in the tz
            // database.
            transitionTime =
                    getPreviousZoneTransitionTime(
                            zone, base, 2 * 60 * 60 * 1000); // check last 2 hours
            if (transitionTime == null) {
                transitionTime =
                        getPreviousZoneTransitionTime(
                                zone, base, 30 * 60 * 60 * 1000); // try last 30 hours
            }
        }
        return transitionTime;
    }

    /**
     * Find the previous zone transition within the specified duration. Note: This method is only
     * used when TimeZone is NOT a BasicTimeZone.
     *
     * @param tz The time zone.
     * @param base The base time, inclusive.
     * @param duration The range of time evaluated.
     * @return The time of the previous zone transition, or null if not available.
     */
    private static Long getPreviousZoneTransitionTime(TimeZone tz, long base, long duration) {
        assert duration > 0;

        long upper = base;
        long lower = base - duration - 1;
        int offsetU = tz.getOffset(upper);
        int offsetL = tz.getOffset(lower);
        if (offsetU == offsetL) {
            return null;
        }
        return findPreviousZoneTransitionTime(tz, offsetU, upper, lower);
    }

    /**
     * The time units used by {@link #findPreviousZoneTransitionTime(TimeZone, int, long, long)} for
     * optimizing transition time binary search.
     */
    private static final int[] FIND_ZONE_TRANSITION_TIME_UNITS = {
        60 * 60 * 1000, // 1 hour
        30 * 60 * 1000, // 30 minutes
        60 * 1000, // 1 minute
        1000, // 1 second
    };

    /**
     * Implementing binary search for zone transition detection, used by {@link
     * #getPreviousZoneTransitionTime(TimeZone, long, long)}
     *
     * @param tz The time zone.
     * @param upperOffset The zone offset at <code>upper</code>
     * @param upper The upper bound, inclusive.
     * @param lower The lower bound, exclusive.
     * @return The time of the previous zone transition, or null if not available.
     */
    private static Long findPreviousZoneTransitionTime(
            TimeZone tz, int upperOffset, long upper, long lower) {
        boolean onUnitTime = false;
        long mid = 0;

        for (int unit : FIND_ZONE_TRANSITION_TIME_UNITS) {
            long lunits = lower / unit;
            long uunits = upper / unit;
            if (uunits > lunits) {
                mid = ((lunits + uunits + 1) >>> 1) * unit;
                onUnitTime = true;
                break;
            }
        }

        int midOffset;
        if (!onUnitTime) {
            mid = (upper + lower) >>> 1;
        }

        if (onUnitTime) {
            if (mid != upper) {
                midOffset = tz.getOffset(mid);
                if (midOffset != upperOffset) {
                    return findPreviousZoneTransitionTime(tz, upperOffset, upper, mid);
                }
                upper = mid;
            }
            // check mid-1
            mid--;
        } else {
            mid = (upper + lower) >>> 1;
        }

        if (mid == lower) {
            return upper;
        }
        midOffset = tz.getOffset(mid);
        if (midOffset != upperOffset) {
            if (onUnitTime) {
                return upper;
            }
            return findPreviousZoneTransitionTime(tz, upperOffset, upper, mid);
        }
        return findPreviousZoneTransitionTime(tz, upperOffset, mid, lower);
    }

    /**
     * Compute the milliseconds in the day from the fields. This is a value from 0 to 23:59:59.999
     * inclusive, unless fields are out of range, in which case it can be an arbitrary value. This
     * value reflects local zone wall time.
     *
     * @deprecated ICU 60
     */
    @Deprecated
    protected int computeMillisInDay() {
        // Do the time portion of the conversion.

        int millisInDay = 0;

        // Find the best set of fields specifying the time of day.  There
        // are only two possibilities here; the HOUR_OF_DAY or the
        // AM_PM and the HOUR.
        int hourOfDayStamp = stamp[HOUR_OF_DAY];
        int hourStamp = Math.max(stamp[HOUR], stamp[AM_PM]);
        int bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp;

        // Hours
        if (bestStamp != UNSET) {
            if (bestStamp == hourOfDayStamp) {
                // Don't normalize here; let overflow bump into the next period.
                // This is consistent with how we handle other fields.
                millisInDay += internalGet(HOUR_OF_DAY);
            } else {
                // Don't normalize here; let overflow bump into the next period.
                // This is consistent with how we handle other fields.
                millisInDay += internalGet(HOUR);
                millisInDay += 12 * internalGet(AM_PM); // Default works for unset AM_PM
            }
        }

        // We use the fact that unset == 0; we start with millisInDay
        // == HOUR_OF_DAY.
        millisInDay *= 60;
        millisInDay += internalGet(MINUTE); // now have minutes
        millisInDay *= 60;
        millisInDay += internalGet(SECOND); // now have seconds
        millisInDay *= 1000;
        millisInDay += internalGet(MILLISECOND); // now have millis

        return millisInDay;
    }

    /**
     * Compute the milliseconds in the day from the fields. The standard value range is from 0 to
     * 23:59:59.999 inclusive. This value reflects local zone wall time.
     *
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    protected long computeMillisInDayLong() {
        // Do the time portion of the conversion.

        long millisInDay = 0;

        // Find the best set of fields specifying the time of day.  There
        // are only two possibilities here; the HOUR_OF_DAY or the
        // AM_PM and the HOUR.
        int hourOfDayStamp = stamp[HOUR_OF_DAY];
        int hourStamp = Math.max(stamp[HOUR], stamp[AM_PM]);
        int bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp;

        // Hours
        if (bestStamp != UNSET) {
            if (bestStamp == hourOfDayStamp) {
                // Don't normalize here; let overflow bump into the next period.
                // This is consistent with how we handle other fields.
                millisInDay += internalGet(HOUR_OF_DAY);
            } else {
                // Don't normalize here; let overflow bump into the next period.
                // This is consistent with how we handle other fields.
                millisInDay += internalGet(HOUR);
                millisInDay += 12 * internalGet(AM_PM); // Default works for unset AM_PM
            }
        }

        // We use the fact that unset == 0; we start with millisInDay
        // == HOUR_OF_DAY.
        millisInDay *= 60;
        millisInDay += internalGet(MINUTE); // now have minutes
        millisInDay *= 60;
        millisInDay += internalGet(SECOND); // now have seconds
        millisInDay *= 1000;
        millisInDay += internalGet(MILLISECOND); // now have millis

        return millisInDay;
    }

    /**
     * This method can assume EXTENDED_YEAR has been set.
     *
     * @param millis milliseconds of the date fields (local midnight millis)
     * @param millisInDay milliseconds of the time fields; may be out or range.
     * @return total zone offset (raw + DST) for the given moment
     * @deprecated ICU 60
     */
    @Deprecated
    protected int computeZoneOffset(long millis, int millisInDay) {
        int[] offsets = new int[2];
        long wall = millis + millisInDay;
        if (zone instanceof BasicTimeZone) {
            LocalOption nonExistingTimeOpt =
                    (skippedWallTime == WALLTIME_FIRST) ? LocalOption.LATTER : LocalOption.FORMER;
            LocalOption duplicatedTimeOpt =
                    (repeatedWallTime == WALLTIME_FIRST) ? LocalOption.FORMER : LocalOption.LATTER;
            ((BasicTimeZone) zone)
                    .getOffsetFromLocal(wall, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
        } else {
            // By default, TimeZone#getOffset behaves WALLTIME_LAST for both.
            zone.getOffset(wall, true, offsets);

            boolean sawRecentNegativeShift = false;
            if (repeatedWallTime == WALLTIME_FIRST) {
                // Check if the given wall time falls into repeated time range
                long tgmt = wall - (offsets[0] + offsets[1]);

                // Any negative zone transition within last 6 hours?
                // Note: The maximum historic negative zone transition is -3 hours in the tz
                // database.
                // 6 hour window would be sufficient for this purpose.
                int offsetBefore6 = zone.getOffset(tgmt - 6 * 60 * 60 * 1000);
                int offsetDelta = (offsets[0] + offsets[1]) - offsetBefore6;

                assert offsetDelta > -6 * 60 * 60 * 1000 : offsetDelta;
                if (offsetDelta < 0) {
                    sawRecentNegativeShift = true;
                    // Negative shift within last 6 hours. When WALLTIME_FIRST is used and the given
                    // wall time falls
                    // into the repeated time range, use offsets before the transition.
                    // Note: If it does not fall into the repeated time range, offsets remain
                    // unchanged below.
                    zone.getOffset(wall + offsetDelta, true, offsets);
                }
            }
            if (!sawRecentNegativeShift && skippedWallTime == WALLTIME_FIRST) {
                // When skipped wall time option is WALLTIME_FIRST,
                // recalculate offsets from the resolved time (non-wall).
                // When the given wall time falls into skipped wall time,
                // the offsets will be based on the zone offsets AFTER
                // the transition (which means, earliest possible interpretation).
                long tgmt = wall - (offsets[0] + offsets[1]);
                zone.getOffset(tgmt, false, offsets);
            }
        }
        return offsets[0] + offsets[1];
    }

    /**
     * This method can assume EXTENDED_YEAR has been set.
     *
     * @param millis milliseconds of the date fields (local midnight millis)
     * @param millisInDay milliseconds of the time fields
     * @return total zone offset (raw + DST) for the given moment
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    protected int computeZoneOffset(long millis, long millisInDay) {
        int[] offsets = new int[2];
        long wall = millis + millisInDay;
        if (zone instanceof BasicTimeZone) {
            LocalOption nonExistingTimeOpt =
                    (skippedWallTime == WALLTIME_FIRST) ? LocalOption.LATTER : LocalOption.FORMER;
            LocalOption duplicatedTimeOpt =
                    (repeatedWallTime == WALLTIME_FIRST) ? LocalOption.FORMER : LocalOption.LATTER;
            ((BasicTimeZone) zone)
                    .getOffsetFromLocal(wall, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
        } else {
            // By default, TimeZone#getOffset behaves WALLTIME_LAST for both.
            zone.getOffset(wall, true, offsets);

            boolean sawRecentNegativeShift = false;
            if (repeatedWallTime == WALLTIME_FIRST) {
                // Check if the given wall time falls into repeated time range
                long tgmt = wall - (offsets[0] + offsets[1]);

                // Any negative zone transition within last 6 hours?
                // Note: The maximum historic negative zone transition is -3 hours in the tz
                // database.
                // 6 hour window would be sufficient for this purpose.
                int offsetBefore6 = zone.getOffset(tgmt - 6 * 60 * 60 * 1000);
                int offsetDelta = (offsets[0] + offsets[1]) - offsetBefore6;

                assert offsetDelta > -6 * 60 * 60 * 1000 : offsetDelta;
                if (offsetDelta < 0) {
                    sawRecentNegativeShift = true;
                    // Negative shift within last 6 hours. When WALLTIME_FIRST is used and the given
                    // wall time falls
                    // into the repeated time range, use offsets before the transition.
                    // Note: If it does not fall into the repeated time range, offsets remain
                    // unchanged below.
                    zone.getOffset(wall + offsetDelta, true, offsets);
                }
            }
            if (!sawRecentNegativeShift && skippedWallTime == WALLTIME_FIRST) {
                // When skipped wall time option is WALLTIME_FIRST,
                // recalculate offsets from the resolved time (non-wall).
                // When the given wall time falls into skipped wall time,
                // the offsets will be based on the zone offsets AFTER
                // the transition (which means, earliest possible interpretation).
                long tgmt = wall - (offsets[0] + offsets[1]);
                zone.getOffset(tgmt, false, offsets);
            }
        }
        return offsets[0] + offsets[1];
    }

    /**
     * Compute the Julian day number as specified by this calendar's fields.
     *
     * @stable ICU 2.0
     */
    protected int computeJulianDay() {

        // We want to see if any of the date fields is newer than the
        // JULIAN_DAY.  If not, then we use JULIAN_DAY.  If so, then we do
        // the normal resolution.  We only use JULIAN_DAY if it has been
        // set by the user.  This makes it possible for the caller to set
        // the calendar to a time and call clear(MONTH) to reset the MONTH
        // to January.  This is legacy behavior.  Without this,
        // clear(MONTH) has no effect, since the internally set JULIAN_DAY
        // is used.
        if (stamp[JULIAN_DAY] >= MINIMUM_USER_STAMP) {
            int bestStamp = newestStamp(ERA, DAY_OF_WEEK_IN_MONTH, UNSET);
            bestStamp = newestStamp(YEAR_WOY, EXTENDED_YEAR, bestStamp);
            bestStamp = newestStamp(ORDINAL_MONTH, ORDINAL_MONTH, bestStamp);
            if (bestStamp <= stamp[JULIAN_DAY]) {
                return internalGet(JULIAN_DAY);
            }
        }

        int bestField = resolveFields(getFieldResolutionTable());
        if (bestField < 0) {
            bestField = DAY_OF_MONTH;
        }

        return handleComputeJulianDay(bestField);
    }

    /**
     * Returns the field resolution array for this calendar. Calendars that define additional fields
     * or change the semantics of existing fields should override this method to adjust the field
     * resolution semantics accordingly. Other subclasses should not override this method.
     *
     * @see #resolveFields
     * @stable ICU 2.0
     */
    protected int[][][] getFieldResolutionTable() {
        return DATE_PRECEDENCE;
    }

    /**
     * Returns the Julian day number of day before the first day of the given month in the given
     * extended year. Subclasses should override this method to implement their calendar system.
     *
     * @param eyear the extended year
     * @param month the zero-based month, or 0 if useMonth is false
     * @param useMonth if false, compute the day before the first day of the given year, otherwise,
     *     compute the day before the first day of the given month
     * @return the Julian day number of the day before the first day of the given month and year
     * @stable ICU 2.0
     */
    protected abstract int handleComputeMonthStart(int eyear, int month, boolean useMonth);

    /**
     * Returns the extended year defined by the current fields. This will use the EXTENDED_YEAR
     * field or the YEAR and supra-year fields (such as ERA) specific to the calendar system,
     * depending on which set of fields is newer.
     *
     * @return the extended year
     * @stable ICU 2.0
     */
    protected abstract int handleGetExtendedYear();

    // (The following method is not called because all existing subclasses
    // override it.  2003-06-11 ICU 2.6 Alan)
    /// CLOVER:OFF
    /**
     * Returns the number of days in the given month of the given extended year of this calendar
     * system. Subclasses should override this method if they can provide a more correct or more
     * efficient implementation than the default implementation in Calendar.
     *
     * @stable ICU 2.0
     */
    protected int handleGetMonthLength(int extendedYear, int month) {
        System.out.printf("handleGetMonthLength(ey=%d, m=%d)", extendedYear, month);
        return handleComputeMonthStart(extendedYear, month + 1, true)
                - handleComputeMonthStart(extendedYear, month, true);
    }

    /// CLOVER:ON

    /**
     * Returns the number of days in the given extended year of this calendar system. Subclasses
     * should override this method if they can provide a more correct or more efficient
     * implementation than the default implementation in Calendar.
     *
     * @stable ICU 2.0
     */
    protected int handleGetYearLength(int eyear) {
        return handleComputeMonthStart(eyear + 1, 0, false)
                - handleComputeMonthStart(eyear, 0, false);
    }

    /**
     * Subclasses that use additional fields beyond those defined in <code>Calendar</code> should
     * override this method to return an <code>int[]</code> array of the appropriate length. The
     * length must be at least <code>BASE_FIELD_COUNT</code> and no more than <code>MAX_FIELD_COUNT
     * </code>.
     *
     * @stable ICU 2.0
     */
    protected int[] handleCreateFields() {
        return new int[BASE_FIELD_COUNT];
    }

    /**
     * Subclasses may override this. Called by handleComputeJulianDay. Returns the default month
     * (0-based) for the year, taking year and era into account. Defaults to 0 (JANUARY) for
     * Gregorian.
     *
     * @param extendedYear the extendedYear, as returned by handleGetExtendedYear
     * @return the default month
     * @draft ICU 3.6 (retain)
     * @see #MONTH
     */
    protected int getDefaultMonthInYear(int extendedYear) {
        return Calendar.JANUARY;
    }

    /**
     * Subclasses may override this. Called by handleComputeJulianDay. Returns the default day
     * (1-based) for the month, taking currently-set year and era into account. Defaults to 1 for
     * Gregorian.
     *
     * @param extendedYear the extendedYear, as returned by handleGetExtendedYear
     * @param month the month, as returned by getDefaultMonthInYear
     * @return the default day of the month
     * @draft ICU 3.6 (retain)
     * @see #DAY_OF_MONTH
     */
    protected int getDefaultDayInMonth(int extendedYear, int month) {
        return 1;
    }

    /**
     * Subclasses may override this. This method calls handleGetMonthLength() to obtain the
     * calendar-specific month length.
     *
     * @stable ICU 2.0
     */
    protected int handleComputeJulianDay(int bestField) {

        boolean useMonth =
                (bestField == DAY_OF_MONTH
                        || bestField == WEEK_OF_MONTH
                        || bestField == DAY_OF_WEEK_IN_MONTH);

        int year;

        if (bestField == WEEK_OF_YEAR && newerField(YEAR_WOY, YEAR) == YEAR_WOY) {
            // Nota Bene!  It is critical that YEAR_WOY be used as the year here, if it is
            // set.  Otherwise, when WOY is the best field, the year may be wrong at the
            // extreme limits of the year.  If YEAR_WOY is not set then it will fall back.
            // TODO: Should resolveField(YEAR_PRECEDENCE) be brought to bear?
            year = internalGet(YEAR_WOY);
        } else {
            year = handleGetExtendedYear();
        }

        internalSet(EXTENDED_YEAR, year);

        if (year > Long.MAX_VALUE / 400) {
            throw new IllegalArgumentException("year is too large");
        }

        int month = useMonth ? internalGetMonth(getDefaultMonthInYear(year)) : 0;

        // Get the Julian day of the day BEFORE the start of this year.
        // If useMonth is true, get the day before the start of the month.
        int julianDay = handleComputeMonthStart(year, month, useMonth);

        if (bestField == DAY_OF_MONTH) {
            if (isSet(DAY_OF_MONTH)) {
                return julianDay + internalGet(DAY_OF_MONTH, getDefaultDayInMonth(year, month));
            } else {
                return julianDay + getDefaultDayInMonth(year, month);
            }
        }

        if (bestField == DAY_OF_YEAR) {
            return julianDay + internalGet(DAY_OF_YEAR);
        }

        int firstDOW = getFirstDayOfWeek(); // Localized fdw

        // At this point julianDay is the 0-based day BEFORE the first day of
        // January 1, year 1 of the given calendar.  If julianDay == 0, it
        // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
        // or Gregorian).

        // At this point we need to process the WEEK_OF_MONTH or
        // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
        // First, perform initial shared computations.  These locate the
        // first week of the period.

        // Get the 0-based localized DOW of day one of the month or year.
        // Valid range 0..6.
        int first = julianDayToDayOfWeek(julianDay + 1) - firstDOW;
        if (first < 0) {
            first += 7;
        }

        // Get zero-based localized DOW, valid range 0..6.  This is the DOW
        // we are looking for.
        int dowLocal = 0;
        switch (resolveFields(DOW_PRECEDENCE)) {
            case DAY_OF_WEEK:
                dowLocal = internalGet(DAY_OF_WEEK) - firstDOW;
                break;
            case DOW_LOCAL:
                dowLocal = internalGet(DOW_LOCAL) - 1;
                break;
        }
        dowLocal = dowLocal % 7;
        if (dowLocal < 0) {
            dowLocal += 7;
        }

        // Find the first target DOW (dowLocal) in the month or year.
        // Actually, it may be just before the first of the month or year.
        // It will be an integer from -5..7.
        int date = 1 - first + dowLocal;

        if (bestField == DAY_OF_WEEK_IN_MONTH) {

            // Adjust the target DOW to be in the month or year.
            if (date < 1) {
                date += 7;
            }

            // The only trickiness occurs if the day-of-week-in-month is
            // negative.
            int dim = internalGet(DAY_OF_WEEK_IN_MONTH, 1);
            if (dim >= 0) {
                date += 7 * (dim - 1);

            } else {
                // Move date to the last of this day-of-week in this month,
                // then back up as needed.  If dim==-1, we don't back up at
                // all.  If dim==-2, we back up once, etc.  Don't back up
                // past the first of the given day-of-week in this month.
                // Note that we handle -2, -3, etc. correctly, even though
                // values < -1 are technically disallowed.
                int m = internalGetMonth(JANUARY);
                int monthLength = handleGetMonthLength(year, m);
                date += ((monthLength - date) / 7 + dim + 1) * 7;
            }
        } else {
            // assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR)

            // Adjust for minimal days in first week
            if ((7 - first) < getMinimalDaysInFirstWeek()) {
                date += 7;
            }

            // Now adjust for the week number.
            date += 7 * (internalGet(bestField) - 1);
        }

        return julianDay + date;
    }

    /**
     * Compute the Julian day of a month of the Gregorian calendar. Subclasses may call this method
     * to perform a Gregorian calendar fields-&gt;millis computation. To perform a Gregorian
     * calendar millis-&gt;fields computation, call computeGregorianFields().
     *
     * @param year extended Gregorian year
     * @param month zero-based Gregorian month
     * @return the Julian day number of the day before the first day of the given month in the given
     *     extended year
     * @see #computeGregorianFields
     * @stable ICU 2.0
     */
    protected int computeGregorianMonthStart(int year, int month) {

        // If the month is out of range, adjust it into range, and
        // modify the extended year value accordingly.
        if (month < 0 || month > 11) {
            int[] rem = new int[1];
            year += floorDivide(month, 12, rem);
            month = rem[0];
        }

        boolean isLeap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
        int y = year - 1;
        // This computation is actually ... + (JAN_1_1_JULIAN_DAY - 3) + 2.
        // Add 2 because Gregorian calendar starts 2 days after Julian
        // calendar.
        int julianDay =
                365 * y
                        + floorDivide(y, 4)
                        - floorDivide(y, 100)
                        + floorDivide(y, 400)
                        + JAN_1_1_JULIAN_DAY
                        - 1;

        // At this point julianDay indicates the day BEFORE the first day
        // of January 1, <eyear> of the Gregorian calendar.
        if (month != 0) {
            julianDay += GREGORIAN_MONTH_COUNT[month][isLeap ? 3 : 2];
        }

        return julianDay;
    }

    // ----------------------------------------------------------------------
    // Subclass API
    // For subclasses to override
    // ----------------------------------------------------------------------

    // (The following method is not called because all existing subclasses
    // override it.  2003-06-11 ICU 2.6 Alan)
    /// CLOVER:OFF
    /**
     * Subclasses may override this method to compute several fields specific to each calendar
     * system. These are:
     *
     * <ul>
     *   <li>ERA
     *   <li>YEAR
     *   <li>MONTH
     *   <li>DAY_OF_MONTH
     *   <li>DAY_OF_YEAR
     *   <li>EXTENDED_YEAR
     * </ul>
     *
     * Subclasses can refer to the DAY_OF_WEEK and DOW_LOCAL fields, which will be set when this
     * method is called. Subclasses can also call the getGregorianXxx() methods to obtain Gregorian
     * calendar equivalents for the given Julian day.
     *
     * <p>In addition, subclasses should compute any subclass-specific fields, that is, fields from
     * BASE_FIELD_COUNT to getFieldCount() - 1.
     *
     * <p>The default implementation in <code>Calendar</code> implements a pure proleptic Gregorian
     * calendar.
     *
     * @stable ICU 2.0
     */
    protected void handleComputeFields(int julianDay) {
        int gmonth = getGregorianMonth();
        internalSet(MONTH, gmonth);
        internalSet(ORDINAL_MONTH, gmonth);
        internalSet(DAY_OF_MONTH, getGregorianDayOfMonth());
        internalSet(DAY_OF_YEAR, getGregorianDayOfYear());
        int eyear = getGregorianYear();
        internalSet(EXTENDED_YEAR, eyear);
        int era = GregorianCalendar.AD;
        if (eyear < 1) {
            era = GregorianCalendar.BC;
            eyear = 1 - eyear;
        }
        internalSet(ERA, era);
        internalSet(YEAR, eyear);
    }

    /// CLOVER:ON

    // ----------------------------------------------------------------------
    // Subclass API
    // For subclasses to call
    // ----------------------------------------------------------------------

    /**
     * Returns the extended year on the Gregorian calendar as computed by <code>
     * computeGregorianFields()</code>.
     *
     * @see #computeGregorianFields
     * @stable ICU 2.0
     */
    protected final int getGregorianYear() {
        return gregorianYear;
    }

    /**
     * Returns the month (0-based) on the Gregorian calendar as computed by <code>
     * computeGregorianFields()</code>.
     *
     * @see #computeGregorianFields
     * @stable ICU 2.0
     */
    protected final int getGregorianMonth() {
        return gregorianMonth;
    }

    /**
     * Returns the day of year (1-based) on the Gregorian calendar as computed by <code>
     * computeGregorianFields()</code>.
     *
     * @see #computeGregorianFields
     * @stable ICU 2.0
     */
    protected final int getGregorianDayOfYear() {
        return gregorianDayOfYear;
    }

    /**
     * Returns the day of month (1-based) on the Gregorian calendar as computed by <code>
     * computeGregorianFields()</code>.
     *
     * @see #computeGregorianFields
     * @stable ICU 2.0
     */
    protected final int getGregorianDayOfMonth() {
        return gregorianDayOfMonth;
    }

    /**
     * {@icu} Returns the number of fields defined by this calendar. Valid field arguments to <code>
     * set()</code> and <code>get()</code> are <code>0..getFieldCount()-1</code>.
     *
     * @stable ICU 2.0
     */
    public final int getFieldCount() {
        return fields.length;
    }

    /**
     * Set a field to a value. Subclasses should use this method when computing fields. It sets the
     * time stamp in the <code>stamp[]</code> array to <code>INTERNALLY_SET</code>. If a field that
     * may not be set by subclasses is passed in, an <code>IllegalArgumentException</code> is
     * thrown. This prevents subclasses from modifying fields that are intended to be
     * calendar-system invariant.
     *
     * @stable ICU 2.0
     */
    protected final void internalSet(int field, int value) {
        if (((1 << field) & internalSetMask) == 0) {
            throw new IllegalStateException("Subclass cannot set " + fieldName(field));
        }
        fields[field] = value;
        stamp[field] = INTERNALLY_SET;
    }

    private static final int[][] GREGORIAN_MONTH_COUNT = {
        // len len2   st  st2
        {31, 31, 0, 0}, // Jan
        {28, 29, 31, 31}, // Feb
        {31, 31, 59, 60}, // Mar
        {30, 30, 90, 91}, // Apr
        {31, 31, 120, 121}, // May
        {30, 30, 151, 152}, // Jun
        {31, 31, 181, 182}, // Jul
        {31, 31, 212, 213}, // Aug
        {30, 30, 243, 244}, // Sep
        {31, 31, 273, 274}, // Oct
        {30, 30, 304, 305}, // Nov
        {31, 31, 334, 335} // Dec
        // len  length of month
        // len2 length of month in a leap year
        // st   days in year before start of month
        // st2  days in year before month in leap year
    };

    /**
     * Determines if the given year is a leap year. Returns true if the given year is a leap year.
     *
     * @param year the given year.
     * @return true if the given year is a leap year; false otherwise.
     * @stable ICU 2.0
     */
    protected static final boolean isGregorianLeapYear(int year) {
        return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
    }

    /**
     * Returns the length of a month of the Gregorian calendar.
     *
     * @param y the extended year
     * @param m the 0-based month number
     * @return the number of days in the given month
     * @stable ICU 2.0
     */
    protected static final int gregorianMonthLength(int y, int m) {
        return GREGORIAN_MONTH_COUNT[m][isGregorianLeapYear(y) ? 1 : 0];
    }

    /**
     * Returns the length of a previous month of the Gregorian calendar.
     *
     * @param y the extended year
     * @param m the 0-based month number
     * @return the number of days in the month previous to the given month
     * @stable ICU 2.0
     */
    protected static final int gregorianPreviousMonthLength(int y, int m) {
        return (m > 0) ? gregorianMonthLength(y, m - 1) : 31;
    }

    /**
     * Divide two long integers, returning the floor of the quotient.
     *
     * <p>Unlike the built-in division, this is mathematically well-behaved. E.g., <code>-1/4</code>
     * =&gt; 0 but <code>floorDivide(-1,4)</code> =&gt; -1.
     *
     * @param numerator the numerator
     * @param denominator a divisor which must be &gt; 0
     * @return the floor of the quotient.
     * @stable ICU 2.0
     */
    protected static final long floorDivide(long numerator, long denominator) {
        // We do this computation in order to handle
        // a numerator of Long.MIN_VALUE correctly
        return (numerator >= 0) ? numerator / denominator : ((numerator + 1) / denominator) - 1;
    }

    /**
     * Divide two integers, returning the floor of the quotient.
     *
     * <p>Unlike the built-in division, this is mathematically well-behaved. E.g., <code>-1/4</code>
     * =&gt; 0 but <code>floorDivide(-1,4)</code> =&gt; -1.
     *
     * @param numerator the numerator
     * @param denominator a divisor which must be &gt; 0
     * @return the floor of the quotient.
     * @stable ICU 2.0
     */
    protected static final int floorDivide(int numerator, int denominator) {
        // We do this computation in order to handle
        // a numerator of Integer.MIN_VALUE correctly
        return (numerator >= 0) ? numerator / denominator : ((numerator + 1) / denominator) - 1;
    }

    /**
     * Divide two integers, returning the floor of the quotient, and the modulus remainder.
     *
     * <p>Unlike the built-in division, this is mathematically well-behaved. E.g., <code>-1/4</code>
     * =&gt; 0 and <code>-1%4</code> =&gt; -1, but <code>floorDivide(-1,4)</code> =&gt; -1 with
     * <code>remainder[0]</code> =&gt; 3.
     *
     * @param numerator the numerator
     * @param denominator a divisor which must be &gt; 0
     * @param remainder an array of at least one element in which the value <code>
     *     numerator mod denominator</code> is returned. Unlike <code>numerator
     * % denominator</code>, this will always be non-negative.
     * @return the floor of the quotient.
     * @stable ICU 2.0
     */
    protected static final int floorDivide(int numerator, int denominator, int[] remainder) {
        if (numerator >= 0) {
            remainder[0] = numerator % denominator;
            return numerator / denominator;
        }
        int quotient = ((numerator + 1) / denominator) - 1;
        remainder[0] = numerator - (quotient * denominator);
        return quotient;
    }

    /**
     * Divide two integers, returning the floor of the quotient, and the modulus remainder.
     *
     * <p>Unlike the built-in division, this is mathematically well-behaved. E.g., <code>-1/4</code>
     * =&gt; 0 and <code>-1%4</code> =&gt; -1, but <code>floorDivide(-1,4)</code> =&gt; -1 with
     * <code>remainder[0]</code> =&gt; 3.
     *
     * @param numerator the numerator
     * @param denominator a divisor which must be &gt; 0
     * @param remainder an array of at least one element in which the value <code>
     *     numerator mod denominator</code> is returned. Unlike <code>numerator
     * % denominator</code>, this will always be non-negative.
     * @return the floor of the quotient.
     * @stable ICU 2.0
     */
    protected static final int floorDivide(long numerator, int denominator, int[] remainder) {
        if (numerator >= 0) {
            remainder[0] = (int) (numerator % denominator);
            return (int) (numerator / denominator);
        }
        int quotient = (int) (((numerator + 1) / denominator) - 1);
        remainder[0] = (int) (numerator - ((long) quotient * denominator));
        return quotient;
    }

    private static final String[] FIELD_NAME = {
        "ERA",
        "YEAR",
        "MONTH",
        "WEEK_OF_YEAR",
        "WEEK_OF_MONTH",
        "DAY_OF_MONTH",
        "DAY_OF_YEAR",
        "DAY_OF_WEEK",
        "DAY_OF_WEEK_IN_MONTH",
        "AM_PM",
        "HOUR",
        "HOUR_OF_DAY",
        "MINUTE",
        "SECOND",
        "MILLISECOND",
        "ZONE_OFFSET",
        "DST_OFFSET",
        "YEAR_WOY",
        "DOW_LOCAL",
        "EXTENDED_YEAR",
        "JULIAN_DAY",
        "MILLISECONDS_IN_DAY",
        "IS_LEAP_MONTH",
        "ORDINAL_MONTH"
    };

    /**
     * Returns a string name for a field, for debugging and exceptions.
     *
     * @stable ICU 2.0
     */
    protected String fieldName(int field) {
        try {
            return FIELD_NAME[field];
        } catch (ArrayIndexOutOfBoundsException e) {
            return "Field " + field;
        }
    }

    /**
     * Converts time as milliseconds to Julian day.
     *
     * @param millis the given milliseconds.
     * @return the Julian day number.
     * @stable ICU 2.0
     */
    protected static final int millisToJulianDay(long millis) {
        return (int) (EPOCH_JULIAN_DAY + floorDivide(millis, ONE_DAY));
    }

    /**
     * Converts Julian day to time as milliseconds.
     *
     * @param julian the given Julian day number.
     * @return time as milliseconds.
     * @stable ICU 2.0
     */
    protected static final long julianDayToMillis(int julian) {
        return (julian - EPOCH_JULIAN_DAY) * ONE_DAY;
    }

    /**
     * Returns the day of week, from SUNDAY to SATURDAY, given a Julian day.
     *
     * @stable ICU 2.0
     */
    protected static final int julianDayToDayOfWeek(int julian) {
        // If julian is negative, then julian%7 will be negative, so we adjust
        // accordingly.  Julian day 0 is Monday.
        int dayOfWeek = (julian + MONDAY) % 7;
        if (dayOfWeek < SUNDAY) {
            dayOfWeek += 7;
        }
        return dayOfWeek;
    }

    /**
     * Returns the current milliseconds without recomputing.
     *
     * @stable ICU 2.0
     */
    protected final long internalGetTimeInMillis() {
        return time;
    }

    /**
     * {@icu} Returns the calendar type name string for this Calendar object. The returned string is
     * the legacy ICU calendar attribute value, for example, "gregorian" or "japanese".
     *
     * <p>See type="old type name" for the calendar attribute of locale IDs at
     * http://www.unicode.org/reports/tr35/#Key_Type_Definitions
     *
     * @return legacy calendar type name string
     * @stable ICU 3.8
     */
    public String getType() {
        return "unknown";
    }

    /**
     * Returns if two digit representation of year in this calendar type customarily implies a
     * default century (i.e. 03 -&gt; 2003). The default implementation returns <code>true</code>. A
     * subclass may return <code>false</code> if such practice is not applicable (for example,
     * Chinese calendar and Japanese calendar).
     *
     * @return <code>true</code> if this calendar has a default century.
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    public boolean haveDefaultCentury() {
        return true;
    }

    // -------- BEGIN ULocale boilerplate --------

    /**
     * {@icu} Returns the locale that was used to create this object, or null. This may may differ
     * from the locale requested at the time of this object's creation. For example, if an object is
     * created for locale {@code en_US_CALIFORNIA}, the actual data may be drawn from {@code en}
     * (the <i>actual</i> locale), and {@code en_US} may be the most specific locale that exists
     * (the <i>valid</i> locale).
     *
     * <p>Note: This method will be implemented in ICU 3.0; ICU 2.8 contains a partial preview
     * implementation. The <i>actual</i> locale is returned correctly, but the <i>valid</i> locale
     * is not, in most cases.
     *
     * @param type type of information requested, either {@link
     *     com.ibm.icu.util.ULocale#VALID_LOCALE} or {@link com.ibm.icu.util.ULocale#ACTUAL_LOCALE}.
     * @return the information specified by <i>type</i>, or null if this object was not constructed
     *     from locale data.
     * @see com.ibm.icu.util.ULocale
     * @see com.ibm.icu.util.ULocale#VALID_LOCALE
     * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
     * @draft ICU 2.8 (retain)
     */
    public final ULocale getLocale(ULocale.Type type) {
        return type == ULocale.ACTUAL_LOCALE ? this.actualLocale : this.validLocale;
    }

    /**
     * Set information about the locales that were used to create this object. If the object was not
     * constructed from locale data, both arguments should be set to null. Otherwise, neither should
     * be null. The actual locale must be at the same level or less specific than the valid locale.
     * This method is intended for use by factories or other entities that create objects of this
     * class.
     *
     * @param valid the most specific locale containing any resource data, or null
     * @param actual the locale containing data used to construct this object, or null
     * @see com.ibm.icu.util.ULocale
     * @see com.ibm.icu.util.ULocale#VALID_LOCALE
     * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
     */
    final void setLocale(ULocale valid, ULocale actual) {
        // Change the following to an assertion later
        if ((valid == null) != (actual == null)) {
            /// CLOVER:OFF
            throw new IllegalArgumentException();
            /// CLOVER:ON
        }
        // Another check we could do is that the actual locale is at
        // the same level or less specific than the valid locale.
        this.validLocale = valid;
        this.actualLocale = actual;
    }

    /**
     * The most specific locale containing any resource data, or null.
     *
     * @see com.ibm.icu.util.ULocale
     */
    private ULocale validLocale;

    /**
     * The locale containing data used to construct this object, or null.
     *
     * @see com.ibm.icu.util.ULocale
     */
    private ULocale actualLocale;

    // -------- END ULocale boilerplate --------
}
